Tibor17 commented on a change in pull request #300: URL: https://github.com/apache/maven-surefire/pull/300#discussion_r436660313
########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); Review comment: The view in GH is not enough. You have to see whole nested class `Runnable`. ########## File path: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java ########## @@ -129,7 +159,7 @@ private final void setTrueOptions( SocketOption<Boolean>... options ) @Override public String getForkNodeConnectionString() { - return "tcp://" + localHost + ":" + localPort; + return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?sessionId=" + sessionId ); Review comment: We do not know if it is localhost. Nothing must be hard coded in this place. Only one point in whole `SurefireForkChannel` can do it and it is the constructor at the [Line 93](https://github.com/apache/maven-surefire/blob/ea4020701cb2c53545a0ae3a2820938195f4077f/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java#L93). We provide abstraction and we provide two default implementations but it does not mean that later it would not be foreign IP address since some user inherited this class overriding the constructor. This reimplementation can be done in the POM `<forkNode implementation=""/>`. This connection string goes to the properties file and to the fork JVM. ########## File path: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java ########## @@ -129,7 +159,7 @@ private final void setTrueOptions( SocketOption<Boolean>... options ) @Override public String getForkNodeConnectionString() { - return "tcp://" + localHost + ":" + localPort; + return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?sessionId=" + sessionId ); Review comment: It's the best programming approach to set the value in one place of the class and reference the value even if it would be the same with hard coded value. We do it with our experiences that the analysis is simple and easy to change. Therefore our suumptions of a constant string `locahost` does not end up in 3 places, nothing but only one. It's later easy to manipulate with such a class and extend it with alternative construcotr, etc. These are the practices which drive us to this design. ########## File path: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java ########## @@ -129,7 +159,7 @@ private final void setTrueOptions( SocketOption<Boolean>... options ) @Override public String getForkNodeConnectionString() { - return "tcp://" + localHost + ":" + localPort; + return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?sessionId=" + sessionId ); Review comment: It's the best programming approach to set the value in one place of the class and reference the value even if it would be the same with hard coded value. We do it with our experiences that the analysis is simple and easy to change. Therefore our asumptions of a constant string `locahost` does not end up in 3 places, nothing but only one. It's later easy to manipulate with such a class and extend it with alternative construcotr, etc. These are the practices which drive us to this design. ########## File path: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java ########## @@ -129,7 +159,7 @@ private final void setTrueOptions( SocketOption<Boolean>... options ) @Override public String getForkNodeConnectionString() { - return "tcp://" + localHost + ":" + localPort; + return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?sessionId=" + sessionId ); Review comment: It's the best programming approach to set the value in one place of the class and reference the value even if it would be the same with hard coded value. We do it with our experiences that the analysis is simple and easy to change. Therefore our assumptions of a constant string `locahost` does not end up in 3 places, nothing but only one. It's later easy to manipulate with such a class and extend it with alternative construcotr, etc. These are the practices which drive us to this design. ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + server.connectToClient(); + } + } + + @Test( timeout = 10_000L ) + public void shouldNotAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + URI connectionString = new URI( server.getForkNodeConnectionString() ); + client.connect( "tcp://" + connectionString.getHost() + ":" + connectionString.getPort() + + "?sessionId=6ba7b812-9dad-11d1-80b4-00c04fd430c8" ); + } + catch ( Exception e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + + e.expect( IOException.class ); Review comment: This asserts the failure at next line 262 `server.connectToClient();` ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + server.connectToClient(); + } + } + + @Test( timeout = 10_000L ) + public void shouldNotAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + URI connectionString = new URI( server.getForkNodeConnectionString() ); + client.connect( "tcp://" + connectionString.getHost() + ":" + connectionString.getPort() + + "?sessionId=6ba7b812-9dad-11d1-80b4-00c04fd430c8" ); + } + catch ( Exception e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + + e.expect( IOException.class ); Review comment: See the [commit](https://github.com/apache/maven-surefire/pull/300/commits/c6f52fbb756482b0dfc3e5fae73c434b192db1fd) which explains that we did not expect an exception within the Thread. We expect it in server's facility verifying the session id returned by the client. Now the client's Thread also has to be running without exception. ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + server.connectToClient(); + } + } + + @Test( timeout = 10_000L ) + public void shouldNotAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + URI connectionString = new URI( server.getForkNodeConnectionString() ); + client.connect( "tcp://" + connectionString.getHost() + ":" + connectionString.getPort() + + "?sessionId=6ba7b812-9dad-11d1-80b4-00c04fd430c8" ); + } + catch ( Exception e ) + { + e.printStackTrace(); Review comment: See the [commit](https://github.com/apache/maven-surefire/pull/300/commits/c6f52fbb756482b0dfc3e5fae73c434b192db1fd) which explains that we did not expect an exception within the Thread. We expect it in server's facility verifying the session id returned by the client. Now the client's Thread also has to be running without exception. ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); Review comment: See the [commit](https://github.com/apache/maven-surefire/pull/300/commits/c6f52fbb756482b0dfc3e5fae73c434b192db1fd) which explains that we did not expect an exception within the Thread. We expect it in server's facility verifying the session id returned by the client. Now the client's Thread also has to be running without exception. ########## File path: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java ########## @@ -113,6 +124,25 @@ public void connectToClient() throws IOException } } + private void authenticate() throws InterruptedException, ExecutionException, IOException + { + ByteBuffer hash = ByteBuffer.allocate( UUID_STRING_LENGTH ); Review comment: @eolivelli done! ########## File path: surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java ########## @@ -119,4 +129,23 @@ private final void setTrueOptions( SocketOption<Boolean>... options ) } } } + + private UUID parseAuth( URI uri ) Review comment: @eolivelli done! ########## File path: surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java ########## @@ -362,6 +372,83 @@ public void run() throws Throwable } } + @Test + public void shouldAuthenticate() throws Exception + { + mockStatic( ForkedBooter.class ); + + doCallRealMethod() + .when( ForkedBooter.class, "lookupDecoderFactory", anyString() ); + + try ( final ServerSocketChannel server = ServerSocketChannel.open() ) + { + if ( server.supportedOptions().contains( SO_REUSEADDR ) ) + { + server.setOption( SO_REUSEADDR, true ); + } + + if ( server.supportedOptions().contains( TCP_NODELAY ) ) + { + server.setOption( TCP_NODELAY, true ); + } + + if ( server.supportedOptions().contains( SO_KEEPALIVE ) ) Review comment: @eolivelli @olamy Removed in test. Done! ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + server.connectToClient(); + } + } + + @Test( timeout = 10_000L ) + public void shouldNotAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + URI connectionString = new URI( server.getForkNodeConnectionString() ); + client.connect( "tcp://127.0.0.1:" + connectionString.getPort() Review comment: @olamy @eolivelli Used another name `sessionId`. ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + server.connectToClient(); + } + } + + @Test( timeout = 10_000L ) + public void shouldNotAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + URI connectionString = new URI( server.getForkNodeConnectionString() ); + client.connect( "tcp://127.0.0.1:" + connectionString.getPort() Review comment: @michael-o @eolivelli Used another name `sessionId`. ########## File path: surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java ########## @@ -362,6 +372,83 @@ public void run() throws Throwable } } + @Test + public void shouldAuthenticate() throws Exception + { + mockStatic( ForkedBooter.class ); + + doCallRealMethod() + .when( ForkedBooter.class, "lookupDecoderFactory", anyString() ); + + try ( final ServerSocketChannel server = ServerSocketChannel.open() ) + { + if ( server.supportedOptions().contains( SO_REUSEADDR ) ) + { + server.setOption( SO_REUSEADDR, true ); + } + + if ( server.supportedOptions().contains( TCP_NODELAY ) ) + { + server.setOption( TCP_NODELAY, true ); + } + + if ( server.supportedOptions().contains( SO_KEEPALIVE ) ) Review comment: @eolivelli Removed in test. Done! ########## File path: surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java ########## @@ -362,6 +372,83 @@ public void run() throws Throwable } } + @Test + public void shouldAuthenticate() throws Exception + { + mockStatic( ForkedBooter.class ); + + doCallRealMethod() + .when( ForkedBooter.class, "lookupDecoderFactory", anyString() ); + + try ( final ServerSocketChannel server = ServerSocketChannel.open() ) + { + if ( server.supportedOptions().contains( SO_REUSEADDR ) ) + { + server.setOption( SO_REUSEADDR, true ); + } + + if ( server.supportedOptions().contains( TCP_NODELAY ) ) + { + server.setOption( TCP_NODELAY, true ); + } + + if ( server.supportedOptions().contains( SO_KEEPALIVE ) ) + { + server.setOption( SO_KEEPALIVE, true ); + } + + server.bind( new InetSocketAddress( 0 ) ); + int serverPort = ( (InetSocketAddress) server.getLocalAddress() ).getPort(); + final UUID uuid = UUID.randomUUID(); + String url = "tcp://127.0.0.1:" + serverPort + "?auth=" + uuid; Review comment: @michael-o Done! ########## File path: surefire-booter/src/test/java/org/apache/maven/surefire/booter/ForkedBooterMockTest.java ########## @@ -385,4 +472,26 @@ public Object answer( InvocationOnMock invocation ) } ).when( ShutdownHookUtils.class, "addShutDownHook", any( Thread.class ) ); invokeMethod( booter, "flushEventChannelOnExit" ); } + + @Test + public void shouldParseUUID() throws Exception + { + SurefireMasterProcessChannelProcessorFactory factory = new SurefireMasterProcessChannelProcessorFactory(); + UUID uuid = UUID.randomUUID(); + URI uri = new URI( "tcp://127.0.0.1:12345?auth=" + uuid ); Review comment: @michael-o Done! ########## File path: maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/extensions/SurefireForkChannel.java ########## @@ -129,7 +159,7 @@ private final void setTrueOptions( SocketOption<Boolean>... options ) @Override public String getForkNodeConnectionString() { - return "tcp://" + localHost + ":" + localPort; + return "tcp://" + localHost + ":" + localPort + ( sessionId == null ? "" : "?sessionId=" + sessionId ); Review comment: @michael-o used InetAddress. ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); Review comment: @michael-o see whole block of code https://github.com/apache/maven-surefire/blob/c6f52fbb756482b0dfc3e5fae73c434b192db1fd/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java#L235-L259 ########## File path: maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/extensions/E2ETest.java ########## @@ -184,4 +192,74 @@ public void close() .isPositive() .isLessThanOrEqualTo( 6_000L ); } + + @Test( timeout = 10_000L ) + public void shouldAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + client.connect( server.getForkNodeConnectionString() ); + } + catch ( IOException e ) + { + e.printStackTrace(); + throw new RuntimeException( e ); + } + } + }; + t.setDaemon( true ); + t.start(); + server.connectToClient(); + } + } + + @Test( timeout = 10_000L ) + public void shouldNotAuthenticateClient() throws Exception + { + ForkNodeArguments forkNodeArguments = mock( ForkNodeArguments.class ); + when( forkNodeArguments.getSessionId() ).thenReturn( UUID.randomUUID() ); + + try ( SurefireForkChannel server = new SurefireForkChannel( forkNodeArguments ) ) + { + Thread t = new Thread() + { + @Override + public void run() + { + SurefireMasterProcessChannelProcessorFactory client + = new SurefireMasterProcessChannelProcessorFactory(); + try + { + URI connectionString = new URI( server.getForkNodeConnectionString() ); + client.connect( "tcp://" + connectionString.getHost() + ":" + connectionString.getPort() + + "?sessionId=6ba7b812-9dad-11d1-80b4-00c04fd430c8" ); + } + catch ( Exception e ) + { + e.printStackTrace(); Review comment: @michael-o This is no more printed. Handled with `Future.get()`. ########## File path: surefire-booter/src/main/java/org/apache/maven/surefire/booter/spi/SurefireMasterProcessChannelProcessorFactory.java ########## @@ -75,6 +79,12 @@ public void connect( String channelConfig ) throws IOException clientSocketChannel = open( withFixedThreadPool( 2, newDaemonThreadFactory() ) ); setTrueOptions( SO_REUSEADDR, TCP_NODELAY, SO_KEEPALIVE ); clientSocketChannel.connect( hostAddress ).get(); + UUID sessionId = parseAuth( uri ); + if ( sessionId != null ) + { + ByteBuffer buff = ByteBuffer.wrap( sessionId.toString().getBytes( US_ASCII ) ); Review comment: @michael-o already renamed to buffer. ---------------------------------------------------------------- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. For queries about this service, please contact Infrastructure at: us...@infra.apache.org