You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by hb...@apache.org on 2022/04/01 22:16:51 UTC

[maven-artifact-plugin] branch MARTIFACT-24 updated (2fd0c4d -> e9b18c9)

This is an automated email from the ASF dual-hosted git repository.

hboutemy pushed a change to branch MARTIFACT-24
in repository https://gitbox.apache.org/repos/asf/maven-artifact-plugin.git.


 discard 2fd0c4d  add project.build.outputTimestamp check
 discard 055773a  add more plugins references
 discard a5610dd  add more plugins and document fixes
 discard 46aa60d  display pom.xml location in case of failure to ease fixing
 discard f33ed68  [MARTIFACT-24] add check-buildplan goal to check plugins reproducibility
     new e69d453  [MARTIFACT-24] add check-buildplan goal to check plugins reproducibility
     new f110325  [MARTIFACT-24] log pom.xml location in case of failure to ease fixing
     new e9b18c9  [MARTIFACT-24] add project.build.outputTimestamp check

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (2fd0c4d)
            \
             N -- N -- N   refs/heads/MARTIFACT-24 (e9b18c9)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:

[maven-artifact-plugin] 02/03: [MARTIFACT-24] log pom.xml location in case of failure to ease fixing

Posted by hb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hboutemy pushed a commit to branch MARTIFACT-24
in repository https://gitbox.apache.org/repos/asf/maven-artifact-plugin.git

commit f110325900fc8b8155a3010839c1831c37a4e742
Author: Hervé Boutemy <hb...@apache.org>
AuthorDate: Wed Mar 30 08:46:15 2022 +0200

    [MARTIFACT-24] log pom.xml location in case of failure to ease fixing
---
 .../artifact/buildinfo/CheckBuildPlanMojo.java     | 14 +++++---
 .../buildinfo/not-reproducible-plugins.properties  | 39 +++++++++++++++++++++-
 2 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
index 92d22e0..0ff366e 100644
--- a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
@@ -29,7 +29,6 @@ import java.util.Set;
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 import org.apache.maven.execution.MavenSession;
-import org.apache.maven.lifecycle.DefaultLifecycles;
 import org.apache.maven.lifecycle.LifecycleExecutor;
 import org.apache.maven.lifecycle.MavenExecutionPlan;
 import org.apache.maven.model.Plugin;
@@ -39,16 +38,17 @@ import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
 
 /**
- * Check from buildplan that plugins used don't have know reproducible builds issues.
+ * Check from buildplan that plugins used don't have known reproducible builds issues.
  */
 @Mojo( name = "check-buildplan", threadSafe = true, requiresProject = true )
 public class CheckBuildPlanMojo
     extends AbstractMojo
 {
-    @Component( role = DefaultLifecycles.class )
-    DefaultLifecycles defaultLifecycles;
+    @Parameter( defaultValue = "${project}", readonly = true )
+    private MavenProject project;
 
     @Parameter( defaultValue = "${session}", readonly = true )
     private MavenSession session;
@@ -119,6 +119,12 @@ public class CheckBuildPlanMojo
 
         if ( fail )
         {
+            getLog().info( "current module pom.xml is " + project.getBasedir() + "/pom.xml" );
+            MavenProject parent = project;
+            while ( ( parent = parent.getParent() ) != null )
+            {
+                getLog().info( "        parent pom.xml is " + parent.getBasedir() + "/pom.xml" );
+            }
             throw new MojoExecutionException( "plugin with non-reproducible output found with fix available" );
         }
     }
diff --git a/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties b/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties
index 7cd7316..21f3a69 100644
--- a/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties
+++ b/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties
@@ -18,23 +18,60 @@
 # list of plugins that did not produce reproducible output in the past, with minimum reproducible version
 
 # default org.apache.maven.plugins groupId
+maven-archetype-plugin=3.2.0
+# https://issues.apache.org/jira/browse/ARCHETYPE-590
 maven-assembly-plugin=3.2.0
+# https://issues.apache.org/jira/browse/MASSEMBLY-921
 maven-jar-plugin=3.2.0
+# https://issues.apache.org/jira/browse/MJAR-263
+# https://issues.apache.org/jira/browse/MJAR-275
 maven-ejb-plugin=3.1.0
+# https://issues.apache.org/jira/browse/MEJB-128
 maven-plugin-plugin=3.5.1
+# https://issues.apache.org/jira/browse/MPLUGIN-326
 maven-remote-resources-plugin=1.7.0
+# https://issues.apache.org/jira/browse/MRRESOURCES-114
 maven-shade-plugin=3.2.3
+# https://issues.apache.org/jira/browse/MSHADE-352
 maven-source-plugin=3.2.1
+# https://issues.apache.org/jira/browse/MSOURCES-123
 maven-war-plugin=3.3.1
+# https://issues.apache.org/jira/browse/MWAR-432
 maven-ear-plugin=3.1.0
+# https://issues.apache.org/jira/browse/MEAR-280
+maven-rar-plugin=3.0.0
+# https://issues.apache.org/jira/browse/MRAR-86
+maven-acr-plugin=3.2.0
+# https://issues.apache.org/jira/browse/MACR-53
 
 # plugin-specific groupId
-org.apache.felix+maven-bundle-plugin=5.1.3
+
+org.antlr+antlr3-maven-plugin=fail:https://github.com/antlr/antlr3/pull/195
+
+org.apache.felix+maven-bundle-plugin=5.1.4
+# https://issues.apache.org/jira/browse/FELIX-6495
+# https://issues.apache.org/jira/browse/FELIX-6496
+org.apache.karaf.tooling+karaf-maven-plugin=fail:https://issues.apache.org/jira/browse/KARAF-7367
+org.apache.nifi+nifi-nar-maven-plugin=1.3.4
+# https://issues.apache.org/jira/browse/NIFI-9857
+org.apache.servicemix.tooling+depends-maven-plugin=fail:https://issues.apache.org/jira/browse/SM-5021
+
 org.codehaus.plexus+plexus-component-metadata=2.1.0
+# https://github.com/codehaus-plexus/plexus-containers/issues/27
+org.codehaus.mojo+jaxb2-maven-plugin=fail:https://github.com/mojohaus/jaxb2-maven-plugin
+# 
 org.codehaus.mojo+properties-maven-plugin=1.1.0
+# https://github.com/mojohaus/properties-maven-plugin/pull/75
 org.codehaus.mojo+flatten-maven-plugin=fail:https://github.com/mojohaus/flatten-maven-plugin/issues/256
 
 org.eclipse.sisu+sisu-maven-plugin=0.3.4
+# https://github.com/eclipse/sisu.inject/pull/5
+org.eclipse.jetty+jetty-jspc-maven-plugin=fail:https://github.com/eclipse/jetty.project/
+# 
 org.springframework.boot+springboot-maven-plugin=2.3.0-M4
+#
+
+org.vafer+jdeb=1.10
+# https://github.com/tcurdt/jdeb/pull/363
 
 org.jboss.jandex+jandex-maven-plugin=fail:https://github.com/wildfly/jandex-maven-plugin/pull/35

[maven-artifact-plugin] 01/03: [MARTIFACT-24] add check-buildplan goal to check plugins reproducibility

Posted by hb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hboutemy pushed a commit to branch MARTIFACT-24
in repository https://gitbox.apache.org/repos/asf/maven-artifact-plugin.git

commit e69d45352f2e5564b1d88b214cd4de0e8ac39da1
Author: Hervé Boutemy <hb...@apache.org>
AuthorDate: Wed Mar 30 00:41:44 2022 +0200

    [MARTIFACT-24] add check-buildplan goal to check plugins reproducibility
---
 pom.xml                                            |   2 +-
 .../artifact/buildinfo/CheckBuildPlanMojo.java     | 151 +++++++++++++++++++++
 .../buildinfo/not-reproducible-plugins.properties  |  40 ++++++
 3 files changed, 192 insertions(+), 1 deletion(-)

diff --git a/pom.xml b/pom.xml
index 0bffa4a..cc25afb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -30,7 +30,7 @@
   </parent>
 
   <artifactId>maven-artifact-plugin</artifactId>
-  <version>3.2.1-SNAPSHOT</version>
+  <version>3.3.0-SNAPSHOT</version>
   <packaging>maven-plugin</packaging>
 
   <name>Apache Maven Artifact Plugin</name>
diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
new file mode 100644
index 0000000..92d22e0
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
@@ -0,0 +1,151 @@
+package org.apache.maven.plugins.artifact.buildinfo;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.maven.artifact.versioning.ArtifactVersion;
+import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.lifecycle.DefaultLifecycles;
+import org.apache.maven.lifecycle.LifecycleExecutor;
+import org.apache.maven.lifecycle.MavenExecutionPlan;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+/**
+ * Check from buildplan that plugins used don't have know reproducible builds issues.
+ */
+@Mojo( name = "check-buildplan", threadSafe = true, requiresProject = true )
+public class CheckBuildPlanMojo
+    extends AbstractMojo
+{
+    @Component( role = DefaultLifecycles.class )
+    DefaultLifecycles defaultLifecycles;
+
+    @Parameter( defaultValue = "${session}", readonly = true )
+    private MavenSession session;
+
+    @Component
+    private LifecycleExecutor lifecycleExecutor;
+
+    /** Allow to specify which goals/phases will be used to calculate execution plan. */
+    @Parameter( property = "buildplan.tasks", defaultValue = "deploy" )
+    private String[] tasks;
+
+    protected MavenExecutionPlan calculateExecutionPlan()
+        throws MojoExecutionException
+    {
+        try
+        {
+            return lifecycleExecutor.calculateExecutionPlan( session, tasks );
+        }
+        catch ( Exception e )
+        {
+            throw new MojoExecutionException( "Cannot calculate Maven execution plan" + e.getMessage(), e );
+        }
+    }
+
+    @Override
+    public void execute()
+        throws MojoExecutionException
+    {
+        Properties issues = loadIssues();
+
+        MavenExecutionPlan plan = calculateExecutionPlan();
+
+        Set<String> plugins = new HashSet<>();
+        boolean fail = false;
+        for ( MojoExecution exec : plan.getMojoExecutions() )
+        {
+            Plugin plugin = exec.getPlugin();
+            String id = plugin.getId();
+
+            if ( plugins.add( id ) )
+            {
+                // check reproducibility status
+                String issue = issues.getProperty( plugin.getKey() );
+                if ( issue == null )
+                {
+                    getLog().info( "no known issue with " + id );
+                }
+                else if ( issue.startsWith( "fail:" ) )
+                {
+                    getLog().warn( "plugin without solution " + id );
+                }
+                else
+                {
+                    ArtifactVersion minimum = new DefaultArtifactVersion( issue );
+                    ArtifactVersion version = new DefaultArtifactVersion( plugin.getVersion() );
+                    if ( version.compareTo( minimum ) < 0 )
+                    {
+                        getLog().error( "plugin with non-reproducible output: " + id + ", require minimum " + issue );
+                        fail = true;
+                    }
+                    else
+                    {
+                        getLog().info( "no known issue with " + id + " (>= " + issue + ")" );
+                    }
+                }
+            }
+        }
+
+        if ( fail )
+        {
+            throw new MojoExecutionException( "plugin with non-reproducible output found with fix available" );
+        }
+    }
+
+    private Properties loadIssues()
+        throws MojoExecutionException
+    {
+        try ( InputStream in = getClass().getResourceAsStream( "not-reproducible-plugins.properties" ) )
+        {
+            Properties prop = new Properties();
+            prop.load( in );
+
+            Properties result = new Properties();
+            for ( Map.Entry<Object, Object> entry : prop.entrySet() )
+            {
+                String plugin = entry.getKey().toString().replace( '+', ':' );
+                if ( !plugin.contains( ":" ) )
+                {
+                    plugin = "org.apache.maven.plugins:" + plugin;
+                }
+                result.put( plugin, entry.getValue() );
+            }
+            return result;
+        }
+        catch ( IOException ioe )
+        {
+            throw new MojoExecutionException( "Cannot load issues file", ioe );
+        }
+    }
+}
diff --git a/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties b/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties
new file mode 100644
index 0000000..7cd7316
--- /dev/null
+++ b/src/main/resources/org/apache/maven/plugins/artifact/buildinfo/not-reproducible-plugins.properties
@@ -0,0 +1,40 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you 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.
+
+# list of plugins that did not produce reproducible output in the past, with minimum reproducible version
+
+# default org.apache.maven.plugins groupId
+maven-assembly-plugin=3.2.0
+maven-jar-plugin=3.2.0
+maven-ejb-plugin=3.1.0
+maven-plugin-plugin=3.5.1
+maven-remote-resources-plugin=1.7.0
+maven-shade-plugin=3.2.3
+maven-source-plugin=3.2.1
+maven-war-plugin=3.3.1
+maven-ear-plugin=3.1.0
+
+# plugin-specific groupId
+org.apache.felix+maven-bundle-plugin=5.1.3
+org.codehaus.plexus+plexus-component-metadata=2.1.0
+org.codehaus.mojo+properties-maven-plugin=1.1.0
+org.codehaus.mojo+flatten-maven-plugin=fail:https://github.com/mojohaus/flatten-maven-plugin/issues/256
+
+org.eclipse.sisu+sisu-maven-plugin=0.3.4
+org.springframework.boot+springboot-maven-plugin=2.3.0-M4
+
+org.jboss.jandex+jandex-maven-plugin=fail:https://github.com/wildfly/jandex-maven-plugin/pull/35

[maven-artifact-plugin] 03/03: [MARTIFACT-24] add project.build.outputTimestamp check

Posted by hb...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

hboutemy pushed a commit to branch MARTIFACT-24
in repository https://gitbox.apache.org/repos/asf/maven-artifact-plugin.git

commit e9b18c9e14d424b2ce8a0d8dbe6c6fa03029df50
Author: Hervé Boutemy <hb...@apache.org>
AuthorDate: Fri Apr 1 19:29:01 2022 +0200

    [MARTIFACT-24] add project.build.outputTimestamp check
---
 .../artifact/buildinfo/AbstractBuildinfoMojo.java  |  6 +-
 .../artifact/buildinfo/CheckBuildPlanMojo.java     | 69 +++++++++++++++++++++-
 2 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java
index ae0afc3..ab29a35 100644
--- a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/AbstractBuildinfoMojo.java
@@ -146,17 +146,19 @@ public abstract class AbstractBuildinfoMojo
             }
 
             // check if timestamp well defined in a project from reactor
+            boolean parentInReactor = false;
             MavenProject reactorParent = project;
             while ( reactorProjects.contains( reactorParent.getParent() ) )
             {
+                parentInReactor = true;
                 reactorParent = reactorParent.getParent();
             }
             String prop =
                 reactorParent.getOriginalModel().getProperties().getProperty( "project.build.outputTimestamp" );
             if ( prop == null )
             {
-                getLog().warn( "project.build.outputTimestamp property should not be inherited "
-                    + "but defined in parent POM from reactor " + reactorParent.getFile() );
+                getLog().error( "project.build.outputTimestamp property should not be inherited but defined in "
+                    + ( parentInReactor ? "parent POM from reactor " : "POM " ) + reactorParent.getFile() );
             }
         }
 
diff --git a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
index 0ff366e..983563e 100644
--- a/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
+++ b/src/main/java/org/apache/maven/plugins/artifact/buildinfo/CheckBuildPlanMojo.java
@@ -21,11 +21,15 @@ package org.apache.maven.plugins.artifact.buildinfo;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 
+import org.apache.maven.archiver.MavenArchiver;
 import org.apache.maven.artifact.versioning.ArtifactVersion;
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 import org.apache.maven.execution.MavenSession;
@@ -42,11 +46,16 @@ import org.apache.maven.project.MavenProject;
 
 /**
  * Check from buildplan that plugins used don't have known reproducible builds issues.
+ *
+ * @since 3.3.0
  */
 @Mojo( name = "check-buildplan", threadSafe = true, requiresProject = true )
 public class CheckBuildPlanMojo
     extends AbstractMojo
 {
+    @Parameter( defaultValue = "${reactorProjects}", required = true, readonly = true )
+    private List<MavenProject> reactorProjects;
+
     @Parameter( defaultValue = "${project}", readonly = true )
     private MavenProject project;
 
@@ -60,6 +69,14 @@ public class CheckBuildPlanMojo
     @Parameter( property = "buildplan.tasks", defaultValue = "deploy" )
     private String[] tasks;
 
+    /**
+     * Timestamp for reproducible output archive entries, either formatted as ISO 8601
+     * <code>yyyy-MM-dd'T'HH:mm:ssXXX</code> or as an int representing seconds since the epoch (like
+     * <a href="https://reproducible-builds.org/docs/source-date-epoch/">SOURCE_DATE_EPOCH</a>).
+     */
+    @Parameter( defaultValue = "${project.build.outputTimestamp}" )
+    private String outputTimestamp;
+
     protected MavenExecutionPlan calculateExecutionPlan()
         throws MojoExecutionException
     {
@@ -77,12 +94,14 @@ public class CheckBuildPlanMojo
     public void execute()
         throws MojoExecutionException
     {
+        boolean fail = hasBadOutputTimestamp();
+        // TODO check maven-jar-plugin module-info.class?
+
         Properties issues = loadIssues();
 
         MavenExecutionPlan plan = calculateExecutionPlan();
 
         Set<String> plugins = new HashSet<>();
-        boolean fail = false;
         for ( MojoExecution exec : plan.getMojoExecutions() )
         {
             Plugin plugin = exec.getPlugin();
@@ -121,17 +140,61 @@ public class CheckBuildPlanMojo
         {
             getLog().info( "current module pom.xml is " + project.getBasedir() + "/pom.xml" );
             MavenProject parent = project;
-            while ( ( parent = parent.getParent() ) != null )
+            while ( true )
             {
+                parent = parent.getParent();
+                if ( ( parent == null ) || !reactorProjects.contains( parent ) )
+                {
+                    break;
+                }
                 getLog().info( "        parent pom.xml is " + parent.getBasedir() + "/pom.xml" );
             }
-            throw new MojoExecutionException( "plugin with non-reproducible output found with fix available" );
+            throw new MojoExecutionException( "non-reproducible plugin or configuration found with fix available" );
+        }
+    }
+
+    private boolean hasBadOutputTimestamp()
+    {
+        MavenArchiver archiver = new MavenArchiver();
+        Date timestamp = archiver.parseOutputTimestamp( outputTimestamp );
+        if ( timestamp == null )
+        {
+            getLog().error( "Reproducible Build not activated by project.build.outputTimestamp property: "
+                + "see https://maven.apache.org/guides/mini/guide-reproducible-builds.html" );
+            return true;
+        }
+        else
+        {
+            if ( getLog().isDebugEnabled() )
+            {
+                getLog().debug( "project.build.outputTimestamp = \"" + outputTimestamp + "\" => "
+                    + new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ssXXX" ).format( timestamp ) );
+            }
+
+            // check if timestamp well defined in a project from reactor
+            boolean parentInReactor = false;
+            MavenProject reactorParent = project;
+            while ( reactorProjects.contains( reactorParent.getParent() ) )
+            {
+                parentInReactor = true;
+                reactorParent = reactorParent.getParent();
+            }
+            String prop =
+                reactorParent.getOriginalModel().getProperties().getProperty( "project.build.outputTimestamp" );
+            if ( prop == null )
+            {
+                getLog().error( "project.build.outputTimestamp property should not be inherited but defined in "
+                    + ( parentInReactor ? "parent POM from reactor " : "POM " ) + reactorParent.getFile() );
+                return true;
+            }
         }
+        return false;
     }
 
     private Properties loadIssues()
         throws MojoExecutionException
     {
+        // TODO add issues source override, that downloads from GitHub (disabled by default)
         try ( InputStream in = getClass().getResourceAsStream( "not-reproducible-plugins.properties" ) )
         {
             Properties prop = new Properties();