SLING-8691: Adding a check of the CI status for releases
diff --git a/src/main/java/org/apache/sling/cli/impl/ci/CIStatusValidator.java b/src/main/java/org/apache/sling/cli/impl/ci/CIStatusValidator.java
new file mode 100644
index 0000000..151f458
--- /dev/null
+++ b/src/main/java/org/apache/sling/cli/impl/ci/CIStatusValidator.java
@@ -0,0 +1,136 @@
+package org.apache.sling.cli.impl.ci;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.sling.cli.impl.http.HttpClientFactory;
+import org.apache.sling.cli.impl.nexus.Artifact;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+@Component(service = CIStatusValidator.class)
+public class CIStatusValidator {
+
+    public class ValidationResult {
+        private final String message;
+        private final boolean valid;
+
+        public ValidationResult(boolean valid, String message) {
+            this.valid = valid;
+            this.message = message;
+        }
+
+        public String getMessage() {
+            return message;
+        }
+
+        public boolean isValid() {
+            return valid;
+        }
+    }
+
+    private static final Logger log = LoggerFactory.getLogger(CIStatusValidator.class);
+    private DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
+
+    @Reference
+    private HttpClientFactory httpClientFactory;
+
+    private XPathFactory xPathFactory = XPathFactory.newInstance();
+
+    protected JsonObject fetchCIStatus(String ciEndpoint) throws UnsupportedOperationException, IOException {
+        try (CloseableHttpClient client = httpClientFactory.newClient()) {
+            HttpGet get = new HttpGet(ciEndpoint);
+            get.addHeader("Accept", "application/json");
+            try (CloseableHttpResponse response = client.execute(get)) {
+                try (InputStream content = response.getEntity().getContent()) {
+                    InputStreamReader reader = new InputStreamReader(content);
+                    JsonParser parser = new JsonParser();
+                    return parser.parse(reader).getAsJsonObject();
+                }
+            }
+        }
+    }
+
+    protected String getCIEndpoint(Artifact artifact, Path artifactFilePath) {
+        log.trace("getCIEndpoint");
+        String ciEndpoint = null;
+        try {
+            DocumentBuilder builder = builderFactory.newDocumentBuilder();
+            Document xmlDocument = builder.parse(artifactFilePath.toFile());
+            XPath xPath = xPathFactory.newXPath();
+            String url = (String) xPath.compile("/project/scm/url/text()").evaluate(xmlDocument, XPathConstants.STRING);
+            if (url != null && url.trim().length() > 0) {
+
+                url = url.substring(url.indexOf("?p=") + 3);
+                url = url.substring(0, url.indexOf(".git"));
+                log.debug("Extracted REPO: {}", url);
+
+                ciEndpoint = String.format("https://api.github.com/repos/apache/%s/commits/%s-%s/status", url,
+                        artifact.getArtifactId(), artifact.getVersion());
+                log.debug("Loaded CI Endpoint: {}", ciEndpoint);
+            }
+            log.debug("Retrieved SCM URL: {}", url);
+        } catch (XPathExpressionException | SAXException | IOException | ParserConfigurationException e) {
+            log.debug("Failed to extract SCM URL", e);
+        }
+        return ciEndpoint;
+    }
+
+    public ValidationResult isValid(Artifact artifact, Path artifactFilePath) {
+        log.trace("isValid");
+
+        String ciEndpoint = getCIEndpoint(artifact, artifactFilePath);
+        try {
+            JsonObject status = fetchCIStatus(ciEndpoint);
+            List<String> messageEntries = new ArrayList<>();
+
+            JsonArray statuses = status.get("statuses").getAsJsonArray();
+            for (JsonElement it : statuses) {
+                JsonObject item = it.getAsJsonObject();
+                messageEntries.add("\t" + item.get("context").getAsString());
+                messageEntries.add("\t\tState: " + item.get("state").getAsString());
+                messageEntries.add("\t\tDescription: " + item.get("description").getAsString());
+                messageEntries.add("\t\tSee: " + item.get("target_url").getAsString());
+            }
+            String message = messageEntries.stream().collect(Collectors.joining("\n"));
+            if ("success".equals(status.get("state").getAsString())) {
+                return new ValidationResult(true, message);
+            } else {
+
+                return new ValidationResult(false, message);
+            }
+        } catch (UnsupportedOperationException | IOException e) {
+            return new ValidationResult(false, "Failed to get CI Status: " + e.toString());
+        }
+    }
+
+    public boolean shouldCheck(Artifact artifact, Path artifactFilePath) {
+        log.trace("shouldCheck");
+        return "pom".equals(artifact.getType()) && getCIEndpoint(artifact, artifactFilePath) != null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/cli/impl/nexus/RepositoryDownloader.java b/src/main/java/org/apache/sling/cli/impl/nexus/RepositoryDownloader.java
index d6654bd..4ef6418 100644
--- a/src/main/java/org/apache/sling/cli/impl/nexus/RepositoryDownloader.java
+++ b/src/main/java/org/apache/sling/cli/impl/nexus/RepositoryDownloader.java
@@ -136,7 +136,7 @@
         String fileName = relativeFilePath.substring(relativeFilePath.lastIndexOf('/') + 1);
         Path filePath = Files.createFile(artifactFolderPath.resolve(fileName));
         HttpGet get = new HttpGet(repository.getRepositoryURI() + "/" + relativeFilePath);
-        LOGGER.info("Downloading " + get.getURI().toString());
+        LOGGER.debug("Downloading " + get.getURI().toString());
         try (CloseableHttpResponse response = client.execute(get)) {
             try (InputStream content = response.getEntity().getContent()) {
                 IOUtils.copyLarge(content, Files.newOutputStream(filePath));
diff --git a/src/main/java/org/apache/sling/cli/impl/release/VerifyReleasesCommand.java b/src/main/java/org/apache/sling/cli/impl/release/VerifyReleasesCommand.java
index 3e5255a..687e6d5 100644
--- a/src/main/java/org/apache/sling/cli/impl/release/VerifyReleasesCommand.java
+++ b/src/main/java/org/apache/sling/cli/impl/release/VerifyReleasesCommand.java
@@ -24,6 +24,7 @@
 import java.util.Locale;
 
 import org.apache.sling.cli.impl.Command;
+import org.apache.sling.cli.impl.ci.CIStatusValidator;
 import org.apache.sling.cli.impl.nexus.Artifact;
 import org.apache.sling.cli.impl.nexus.LocalRepository;
 import org.apache.sling.cli.impl.nexus.RepositoryDownloader;
@@ -39,14 +40,10 @@
 
 import picocli.CommandLine;
 
-@Component(service = Command.class,
-           property = {
-                   Command.PROPERTY_NAME_COMMAND_GROUP + "=" + VerifyReleasesCommand.GROUP,
-                   Command.PROPERTY_NAME_COMMAND_NAME + "=" + VerifyReleasesCommand.NAME
-           })
-@CommandLine.Command(name = VerifyReleasesCommand.NAME,
-                     description = "Downloads the staging repository and verifies the artifacts' signatures and hashes.",
-                     subcommands = CommandLine.HelpCommand.class)
+@Component(service = Command.class, property = {
+        Command.PROPERTY_NAME_COMMAND_GROUP + "=" + VerifyReleasesCommand.GROUP,
+        Command.PROPERTY_NAME_COMMAND_NAME + "=" + VerifyReleasesCommand.NAME })
+@CommandLine.Command(name = VerifyReleasesCommand.NAME, description = "Downloads the staging repository and verifies the artifacts' signatures and hashes.", subcommands = CommandLine.HelpCommand.class)
 public class VerifyReleasesCommand implements Command {
 
     private static final Logger LOGGER = LoggerFactory.getLogger(VerifyReleasesCommand.class);
@@ -64,11 +61,12 @@
     private PGPSignatureValidator pgpSignatureValidator;
 
     @Reference
+    private CIStatusValidator ciStatusValidator;
+
+    @Reference
     private HashValidator hashValidator;
 
-    @CommandLine.Option(names = {"-r", "--repository"},
-                        description = "Nexus repository id",
-                        required = true)
+    @CommandLine.Option(names = { "-r", "--repository" }, description = "Nexus repository id", required = true)
     private Integer repositoryId;
 
     @CommandLine.Mixin
@@ -76,34 +74,68 @@
 
     @Override
     public void run() {
+        int checksRun = 0;
+        int failedChecks = 0;
         try {
             LocalRepository repository = repositoryDownloader.download(stagingRepositoryFinder.find(repositoryId));
             Path repositoryRootPath = repository.getRootFolder();
             for (Artifact artifact : repository.getArtifacts()) {
                 Path artifactFilePath = repositoryRootPath.resolve(artifact.getRepositoryRelativePath());
                 Path artifactSignaturePath = repositoryRootPath.resolve(artifact.getRepositoryRelativeSignaturePath());
-                PGPSignatureValidator.ValidationResult ValidationResult = pgpSignatureValidator.verify(artifactFilePath,
+                PGPSignatureValidator.ValidationResult validationResult = pgpSignatureValidator.verify(artifactFilePath,
                         artifactSignaturePath);
+                checksRun++;
+                if (!validationResult.isValid()) {
+                    failedChecks++;
+                }
                 HashValidator.ValidationResult sha1validationResult = hashValidator.validate(artifactFilePath,
                         repositoryRootPath.resolve(artifact.getRepositoryRelativeSha1SumPath()), "SHA-1");
+                checksRun++;
+                if (!sha1validationResult.isValid()) {
+                    failedChecks++;
+                }
                 HashValidator.ValidationResult md5validationResult = hashValidator.validate(artifactFilePath,
                         repositoryRootPath.resolve(artifact.getRepositoryRelativeMd5SumPath()), "MD5");
+                checksRun++;
+                if (!md5validationResult.isValid()) {
+                    failedChecks++;
+                }
                 LOGGER.info("\n" + artifactFilePath.getFileName().toString());
-                PGPPublicKey key = ValidationResult.getKey();
-                LOGGER.info("GPG: {}", ValidationResult.isValid() ? String.format("signed by %s with key (id=0x%X; " +
-                        "fingerprint=%s)", getKeyUserId(key), key.getKeyID(),
-                        Hex.toHexString(key.getFingerprint()).toUpperCase(Locale.US)) : "INVALID");
+                PGPPublicKey key = validationResult.getKey();
+                LOGGER.info("GPG: {}", validationResult.isValid()
+                        ? String.format("signed by %s with key (id=0x%X; " + "fingerprint=%s)", getKeyUserId(key),
+                                key.getKeyID(), Hex.toHexString(key.getFingerprint()).toUpperCase(Locale.US))
+                        : "INVALID");
                 LOGGER.info("SHA-1: {}",
-                        sha1validationResult.isValid() ? String.format("VALID (%s)", sha1validationResult.getActualHash()) :
-                                String.format("INVALID (expected %s, got %s)", sha1validationResult.getExpectedHash(),
+                        sha1validationResult.isValid()
+                                ? String.format("VALID (%s)", sha1validationResult.getActualHash())
+                                : String.format("INVALID (expected %s, got %s)", sha1validationResult.getExpectedHash(),
                                         sha1validationResult.getActualHash()));
-                LOGGER.info("MD-5: {}", md5validationResult.isValid() ? String.format("VALID (%s)", md5validationResult.getActualHash()) :
-                        String.format("INVALID (expected %s, got %s)", md5validationResult.getExpectedHash(),
-                                md5validationResult.getActualHash()));
+                LOGGER.info("MD-5: {}",
+                        md5validationResult.isValid() ? String.format("VALID (%s)", md5validationResult.getActualHash())
+                                : String.format("INVALID (expected %s, got %s)", md5validationResult.getExpectedHash(),
+                                        md5validationResult.getActualHash()));
+
+                if (ciStatusValidator.shouldCheck(artifact, artifactFilePath)) {
+                    CIStatusValidator.ValidationResult ciValidationResult = ciStatusValidator.isValid(artifact,
+                            artifactFilePath);
+                    LOGGER.info("CI Status: {}",
+                            ciValidationResult.isValid() ? String.format("VALID: \n%s", ciValidationResult.getMessage())
+                                    : String.format("INVALID: \n%s", ciValidationResult.getMessage()));
+                    checksRun++;
+                    if (!ciValidationResult.isValid()) {
+                        failedChecks++;
+                    }
+                }
             }
+
         } catch (IOException e) {
             LOGGER.error("Command execution failed.", e);
         }
+
+        LOGGER.info("\n\nRelease Summary: {}\n\n",
+                failedChecks == 0 ? String.format("VALID (%d checks executed)", checksRun)
+                        : String.format("INVALID (%d of %d checks failed)", failedChecks, checksRun));
     }
 
     private String getKeyUserId(PGPPublicKey key) {
diff --git a/src/test/java/org/apache/sling/cli/impl/ci/CIStatusValidatorTest.java b/src/test/java/org/apache/sling/cli/impl/ci/CIStatusValidatorTest.java
new file mode 100644
index 0000000..7320c0b
--- /dev/null
+++ b/src/test/java/org/apache/sling/cli/impl/ci/CIStatusValidatorTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+package org.apache.sling.cli.impl.ci;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+
+import org.apache.sling.cli.impl.ci.CIStatusValidator.ValidationResult;
+import org.apache.sling.cli.impl.nexus.Artifact;
+import org.junit.Test;
+
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class CIStatusValidatorTest {
+
+    private CIStatusValidator validator = new CIStatusValidator() {
+
+        protected JsonObject fetchCIStatus(String ciEndpoint) throws UnsupportedOperationException, IOException {
+            InputStreamReader reader = null;
+            if ("https://api.github.com/repos/apache/sling-repo-pom/commits/repo-pom-1.0/status".equals(ciEndpoint)) {
+                reader = new InputStreamReader(CIStatusValidatorTest.class.getResourceAsStream("/ci/failure.json"));
+            } else if ("https://api.github.com/repos/apache/sling-repo-pom/commits/successful-pom-1.0/status"
+                    .equals(ciEndpoint)) {
+                reader = new InputStreamReader(CIStatusValidatorTest.class.getResourceAsStream("/ci/success.json"));
+            }
+            JsonParser parser = new JsonParser();
+            return parser.parse(reader).getAsJsonObject();
+        }
+
+    };
+    private static Artifact JAR = new Artifact("org.apache.sling", "sample-artifact", "1.0", "", "jar");
+    private static Artifact NON_REPO_POM_ARTIFACT = new Artifact("org.apache.sling", "no-repo-pom", "1.0", "", "pom");
+    private static Path NON_REPO_POM_FILE;
+    private static Artifact POM_ARTIFACT = new Artifact("org.apache.sling", "repo-pom", "1.0", "", "pom");
+    private static Artifact SUCCESSFUL_POM_ARTIFACT = new Artifact("org.apache.sling", "successful-pom", "1.0", "",
+            "pom");
+    private static Path POM_FILE;
+
+    static {
+        try {
+            URI nonrepo = CIStatusValidatorTest.class.getResource("/ci/no-repo.pom").toURI();
+            NON_REPO_POM_FILE = Path.of(nonrepo);
+            URI repo = CIStatusValidatorTest.class.getResource("/ci/repo.pom").toURI();
+            POM_FILE = Path.of(repo);
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void shouldCheck() {
+        assertFalse(validator.shouldCheck(JAR, null));
+        assertFalse(validator.shouldCheck(NON_REPO_POM_ARTIFACT, NON_REPO_POM_FILE));
+        assertTrue(validator.shouldCheck(POM_ARTIFACT, POM_FILE));
+    }
+
+    @Test
+    public void getCIEndpoint() {
+        assertEquals("https://api.github.com/repos/apache/sling-repo-pom/commits/repo-pom-1.0/status",
+                validator.getCIEndpoint(POM_ARTIFACT, POM_FILE));
+    }
+
+    @Test
+    public void isValid() {
+        ValidationResult invalid = validator.isValid(POM_ARTIFACT, POM_FILE);
+        assertFalse(invalid.isValid());
+        assertNotNull(invalid.getMessage());
+
+        ValidationResult valid = validator.isValid(SUCCESSFUL_POM_ARTIFACT, POM_FILE);
+        assertTrue(valid.isValid());
+        assertNotNull(valid.getMessage());
+    }
+
+}
diff --git a/src/test/resources/ci/failure.json b/src/test/resources/ci/failure.json
new file mode 100644
index 0000000..db1cf40
--- /dev/null
+++ b/src/test/resources/ci/failure.json
@@ -0,0 +1,88 @@
+{
+  "state": "failure",
+  "statuses": [
+    {
+      "url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/statuses/4bb1a239054339e00ed8246fe5c68e35a5205aff",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/21237?v=4",
+      "id": 7551205373,
+      "node_id": "MDEzOlN0YXR1c0NvbnRleHQ3NTUxMjA1Mzcz",
+      "state": "error",
+      "description": "This commit cannot be built",
+      "target_url": "https://builds.apache.org/job/Sling/job/sling-org-apache-sling-clam/job/master/75/display/redirect",
+      "context": "continuous-integration/jenkins/branch",
+      "created_at": "2019-09-01T15:47:52Z",
+      "updated_at": "2019-09-01T15:47:52Z"
+    }
+  ],
+  "sha": "4bb1a239054339e00ed8246fe5c68e35a5205aff",
+  "total_count": 1,
+  "repository": {
+    "id": 146987434,
+    "node_id": "MDEwOlJlcG9zaXRvcnkxNDY5ODc0MzQ=",
+    "name": "sling-org-apache-sling-clam",
+    "full_name": "apache/sling-org-apache-sling-clam",
+    "private": false,
+    "owner": {
+      "login": "apache",
+      "id": 47359,
+      "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ3MzU5",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/47359?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/apache",
+      "html_url": "https://github.com/apache",
+      "followers_url": "https://api.github.com/users/apache/followers",
+      "following_url": "https://api.github.com/users/apache/following{/other_user}",
+      "gists_url": "https://api.github.com/users/apache/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/apache/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/apache/subscriptions",
+      "organizations_url": "https://api.github.com/users/apache/orgs",
+      "repos_url": "https://api.github.com/users/apache/repos",
+      "events_url": "https://api.github.com/users/apache/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/apache/received_events",
+      "type": "Organization",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/apache/sling-org-apache-sling-clam",
+    "description": "Apache Sling Clam",
+    "fork": false,
+    "url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam",
+    "forks_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/forks",
+    "keys_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/teams",
+    "hooks_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/hooks",
+    "issue_events_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/events",
+    "assignees_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/tags",
+    "blobs_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/languages",
+    "stargazers_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/stargazers",
+    "contributors_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/contributors",
+    "subscribers_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/subscribers",
+    "subscription_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/subscription",
+    "commits_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/merges",
+    "archive_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/downloads",
+    "issues_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/labels{/name}",
+    "releases_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/deployments"
+  },
+  "commit_url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/commits/4bb1a239054339e00ed8246fe5c68e35a5205aff",
+  "url": "https://api.github.com/repos/apache/sling-org-apache-sling-clam/commits/4bb1a239054339e00ed8246fe5c68e35a5205aff/status"
+}
diff --git a/src/test/resources/ci/no-repo.pom b/src/test/resources/ci/no-repo.pom
new file mode 100644
index 0000000..ca78c52
--- /dev/null
+++ b/src/test/resources/ci/no-repo.pom
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>35</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>no-repo-pom</artifactId>
+    <version>1.0</version>
+
+    <name>No Repo POM</name>
+    <description>A Sample POM File without a repo</description>
+
+</project>
\ No newline at end of file
diff --git a/src/test/resources/ci/repo.pom b/src/test/resources/ci/repo.pom
new file mode 100644
index 0000000..8e29c6e
--- /dev/null
+++ b/src/test/resources/ci/repo.pom
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+>
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling-bundle-parent</artifactId>
+        <version>35</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>repo-pom</artifactId>
+    <version>1.0</version>
+
+    <name>Repo POM</name>
+    <description>A Sample POM File with a repo</description>
+
+    <scm>
+        <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-repo-pom.git</connection>
+        <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-repo-pom.git</developerConnection>
+        <url>https://gitbox.apache.org/repos/asf?p=sling-repo-pom.git</url>
+        <tag>HEAD</tag>
+    </scm>
+
+</project>
diff --git a/src/test/resources/ci/success.json b/src/test/resources/ci/success.json
new file mode 100644
index 0000000..4829682
--- /dev/null
+++ b/src/test/resources/ci/success.json
@@ -0,0 +1,88 @@
+{
+  "state": "success",
+  "statuses": [
+    {
+      "url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/statuses/0adf15ab25998247ac40c697f6ff2a2841132836",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/21237?v=4",
+      "id": 7597309938,
+      "node_id": "MDEzOlN0YXR1c0NvbnRleHQ3NTk3MzA5OTM4",
+      "state": "success",
+      "description": "This commit looks good",
+      "target_url": "https://builds.apache.org/job/Sling/job/sling-org-apache-sling-file-optimization/job/master/37/display/redirect",
+      "context": "continuous-integration/jenkins/branch",
+      "created_at": "2019-09-07T00:45:50Z",
+      "updated_at": "2019-09-07T00:45:50Z"
+    }
+  ],
+  "sha": "0adf15ab25998247ac40c697f6ff2a2841132836",
+  "total_count": 1,
+  "repository": {
+    "id": 139611124,
+    "node_id": "MDEwOlJlcG9zaXRvcnkxMzk2MTExMjQ=",
+    "name": "sling-org-apache-sling-file-optimization",
+    "full_name": "apache/sling-org-apache-sling-file-optimization",
+    "private": false,
+    "owner": {
+      "login": "apache",
+      "id": 47359,
+      "node_id": "MDEyOk9yZ2FuaXphdGlvbjQ3MzU5",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/47359?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/apache",
+      "html_url": "https://github.com/apache",
+      "followers_url": "https://api.github.com/users/apache/followers",
+      "following_url": "https://api.github.com/users/apache/following{/other_user}",
+      "gists_url": "https://api.github.com/users/apache/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/apache/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/apache/subscriptions",
+      "organizations_url": "https://api.github.com/users/apache/orgs",
+      "repos_url": "https://api.github.com/users/apache/repos",
+      "events_url": "https://api.github.com/users/apache/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/apache/received_events",
+      "type": "Organization",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/apache/sling-org-apache-sling-file-optimization",
+    "description": "Apache Sling File Optimization",
+    "fork": false,
+    "url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization",
+    "forks_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/forks",
+    "keys_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/teams",
+    "hooks_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/hooks",
+    "issue_events_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/events",
+    "assignees_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/tags",
+    "blobs_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/languages",
+    "stargazers_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/stargazers",
+    "contributors_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/contributors",
+    "subscribers_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/subscribers",
+    "subscription_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/subscription",
+    "commits_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/merges",
+    "archive_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/downloads",
+    "issues_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/labels{/name}",
+    "releases_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/deployments"
+  },
+  "commit_url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/commits/0adf15ab25998247ac40c697f6ff2a2841132836",
+  "url": "https://api.github.com/repos/apache/sling-org-apache-sling-file-optimization/commits/0adf15ab25998247ac40c697f6ff2a2841132836/status"
+}
diff --git a/src/test/resources/simplelogger.properties b/src/test/resources/simplelogger.properties
index 620f7c4..bfc244a 100644
--- a/src/test/resources/simplelogger.properties
+++ b/src/test/resources/simplelogger.properties
@@ -16,4 +16,4 @@
 # specific language governing permissions and limitations
 # under the License.
 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-org.slf4j.simpleLogger.defaultLogLevel=warn
+org.slf4j.simpleLogger.defaultLogLevel=trace