{"action":"created","comment":{"url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/comments/28196191","id":28196191,"diff_hunk":"@@ -0,0 +1,491 @@\n+package com.nirima.jenkins.plugins.docker.utils;\n+\n+import com.google.common.base.Strings;\n+import java.util.HashMap;\n+import java.util.Map;\n+import org.apache.commons.lang.StringUtils;\n+\n+/**\n+ * For constructing and validating docker image names, and their respective parts:\n+ * [REGISTRY/][NAMESPACE/]REPOSITORY[:TAG]\n+ * \n+ * Based on Docker v1.5 source\n+ * @author Joshua Griffiths 08/04/2015\n+ */\n+\n+\n+public class DockerImageName {\n+    \n+    private TagName tag;\n+    private RepositoryName repository;\n+    private NamespaceName namespace;\n+    private RegistryName registry;\n+    \n+    // Rather than pass null as String\n+    public DockerImageName() {}\n+    \n+    /**\n+     * De-constructs given image name into relevant objects\n+     * @param imageName Full image name for docker image, or null\n+     */\n+    public DockerImageName(String imageName) throws DockerImageNameException {\n+        if (Strings.isNullOrEmpty(imageName)) {\n+            return;\n+        }\n+        // Split the name into a map and assign each name parts. \n+        // This action will throw an exception if the name contains too many parts.\n+        Map<String, String> nameMap = splitImageName(imageName);\n+        if (nameMap.get(\"registry\") != null) {\n+            registry = new RegistryName(nameMap.get(\"registry\"));\n+        }\n+        if (nameMap.get(\"namespace\") != null) {\n+            namespace = new NamespaceName(nameMap.get(\"namespace\"));\n+        }\n+        if (nameMap.get(\"repository\") != null) {\n+            repository = new RepositoryName(nameMap.get(\"repository\"));\n+        }\n+        if (nameMap.get(\"tag\") != null) {\n+            tag = new TagName(nameMap.get(\"tag\"));\n+        }\n+    }\n+    \n+    public TagName getTag() {\n+        return tag;\n+    }\n+    \n+    public RepositoryName getRepository() {\n+        return repository;\n+    }\n+    \n+    public NamespaceName getNamespace() {\n+        return namespace;\n+    }\n+    \n+    public RegistryName getRegistry() {\n+        return registry;\n+    }\n+    \n+    /**\n+     * @param tag String\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withTag(String tag) {\n+        this.tag = new TagName(tag);\n+        return this;\n+    }\n+    \n+    /**\n+     * @param tag TagName object\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withTag(TagName tag) {\n+        this.tag = tag;\n+        return this;\n+    }\n+    \n+    /**\n+     * @param repository String\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withRepository(String repository) {\n+        this.repository = new RepositoryName(repository);\n+        return this;\n+    }\n+    \n+    /**\n+     * @param repository RepositoryName object\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withRepository(RepositoryName repository) {\n+        this.repository = repository;\n+        return this;\n+    }\n+    \n+    /**\n+     * @param namespace String\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withNamespace(String namespace) {\n+        this.namespace = new NamespaceName(namespace);\n+        return this;\n+    }\n+    \n+    /**\n+     * @param namespace NamespaceName object\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withNamespace(NamespaceName namespace) {\n+        this.namespace = namespace;\n+        return this;\n+    }\n+    \n+    /**\n+     * @param registry String\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withRegistry(String registry) {\n+        this.registry = new RegistryName(registry);\n+        return this;\n+    }\n+    \n+    /**\n+     * @param registry RegistryName object\n+     * @return DockerImageName\n+     */\n+    public DockerImageName withRegistry(RegistryName registry) {\n+        this.registry = registry;\n+        return this;\n+    }\n+    \n+    /**\n+     * Joins the image parts which aren't null to return a (possibly) valid name\n+     * @return String containing image name parts\n+     */\n+    @Override\n+    public String toString() {\n+        String imageNameStr = \"\";\n+        if (registry != null) {\n+            imageNameStr += (registry + \"/\");\n+        }\n+        if (namespace != null) {\n+            imageNameStr += (namespace + \"/\");\n+        }\n+        if (repository != null) {\n+            imageNameStr += repository;\n+        }\n+        if (tag != null) {\n+            imageNameStr += (\":\" + tag);\n+        }\n+        return imageNameStr;\n+    }\n+    \n+    /**\n+     * Verifies whether an image name is valid. It must contain a repository name to be valid.\n+     * @return Validity of image name\n+     */\n+    public boolean isValid() {\n+        return (repository != null && repository.isValid()) && (tag == null || tag.isValid()) \n+                && (registry == null || registry.isValid()) && (namespace == null || namespace.isValid());\n+    }\n+    \n+    /**\n+     * Each populated part of the image name is made valid, if possible\n+     * @return Valid DockerImageName part\n+     */\n+    public DockerImageName makeValid() {\n+        if (registry != null) {\n+            registry = registry.makeValid();\n+        }\n+        if (namespace != null) {\n+            namespace = namespace.makeValid();\n+        }\n+        if (repository != null) {\n+            repository = repository.makeValid();\n+        }\n+        if (tag != null) {\n+            tag = tag.makeValid();\n+        }\n+        return this;\n+    }\n+    \n+    /**\n+     * Checks that image is valid, or throws a helpful exception as to why\n+     */\n+    public void validate() throws DockerImageNameException {\n+        if (repository == null) {\n+            throw new DockerImageNameException(\"Invalid image name: Repository cannot be null\");\n+        }\n+        repository.validate();\n+        if (tag != null) {\n+            tag.validate();\n+        }\n+        if (namespace != null) {\n+            namespace.validate();\n+        }\n+        if (registry != null) {\n+            registry.validate();\n+        }\n+    }\n+    \n+    /**\n+     * Splits image name into name parts as cleanly as possible, avoiding any kind of validation\n+     * @param imageName Full docker image name\n+     * @return Map of image name parts\n+     * @throws DockerImageException\n+     */\n+    private static Map<String, String> splitImageName(String imageName) throws DockerImageNameException {\n+        String registryName = null;\n+        String namespaceName = null;\n+        String repositoryName = null;\n+        String tagName = null;\n+        String repositoryNameWithTag;\n+        \n+        // Check for a schema (://) at the start and ignore it when splitting\n+        String[] nameParts;\n+        int schemaIndex = imageName.lastIndexOf(\"://\");\n+        if (schemaIndex > -1) {\n+            nameParts = imageName.substring(schemaIndex + 3).split(\"/\");\n+            nameParts[0] = imageName.substring(0, schemaIndex + 3) + nameParts[0];\n+        } else {\n+            nameParts = imageName.split(\"/\");\n+        }\n+        \n+        // This should never happen. Ever.\n+        if (nameParts.length == 0) {\n+            throw new DockerImageNameException(\"Cannot parse empty image name\");\n+        }\n+        \n+        Map<String, String> nameMap = new HashMap<String, String>();\n+        // Break up the 3 components with slashes. \n+        switch (nameParts.length) {\n+            case 1: \n+                repositoryNameWithTag = nameParts[0];\n+                break;\n+            case 2:\n+                repositoryNameWithTag = nameParts[1];\n+                if (nameParts[0].contains(\".\") || nameParts[0].contains(\":\") || nameParts[0].equals(\"localhost\")) {\n+                    registryName = nameParts[0];\n+                } else {\n+                    namespaceName = nameParts[0];\n+                }\n+                break;\n+            case 3:\n+                repositoryNameWithTag = nameParts[2];\n+                namespaceName = nameParts[1];\n+                registryName = nameParts[0];\n+                break;\n+            default:\n+                namespaceName = nameParts[nameParts.length - 2];\n+                repositoryNameWithTag = nameParts[nameParts.length - 1];\n+                registryName = \"\";\n+                for (int i = 0; i< (nameParts.length -2); i++) {\n+                    registryName += (nameParts[i] + \"/\");\n+                }\n+            registryName = StringUtils.chop(registryName);\n+        }\n+        int lastColonPoint = repositoryNameWithTag.lastIndexOf(\":\");\n+        // If the first char is a ':', treat it as the repository and we'll validate it later\n+        if (lastColonPoint > 0) {\n+            repositoryName = repositoryNameWithTag.substring(0, lastColonPoint);\n+            tagName = repositoryNameWithTag.substring(lastColonPoint + 1);\n+        }\n+        nameMap.put(\"tag\", tagName);\n+        nameMap.put(\"repository\", StringUtils.defaultIfEmpty(repositoryName, repositoryNameWithTag));\n+        nameMap.put(\"namespace\", namespaceName);\n+        nameMap.put(\"registry\", registryName);\n+        return nameMap;\n+    }\n+    \n+    /**\n+     * Abstract class for each image name part to extend\n+     */\n+    private static abstract class AbstractDockerImageName {\n+        \n+        protected String partName;\n+        protected String partRegexp; // For matching\n+        protected String partInvertedRegexp; // For replacing\n+        \n+        /**\n+         * Abstract class constructor for use in subclasses\n+         * @param namePart \n+         */\n+        public AbstractDockerImageName(String namePart) {\n+            if (namePart == null || namePart.isEmpty()) {\n+                throw new IllegalArgumentException(\"Illegal use of empty/null name part\");\n+            }\n+            partName = namePart;\n+        }\n+        \n+        @Override\n+        public String toString() {\n+            return partName;\n+        }\n+        \n+        /**\n+         * Removes any non-docker-friendly characters from the image part\n+         */\n+        public abstract AbstractDockerImageName makeValid();\n+        \n+        /**\n+         * Checks whether the image name part is valid\n+         * @return whether image name part is valid\n+         */\n+        public abstract boolean isValid();\n+        \n+        /**\n+         * Validates the image name part and throws DockerImageNameException, with reason, if invalid\n+        */\n+        public abstract void validate() throws DockerImageNameException;\n+        \n+        public String spacesToHyphens(String str) {\n+            return str.replaceAll(\"[\\\\s]+\", \"-\");\n+        }\n+    }\n+    \n+    public static class RegistryName extends AbstractDockerImageName {\n+        \n+        /**\n+         * Calls constructor on abstract to set namePart\n+         * @param namePart Part of image name\n+         */\n+        public RegistryName(String namePart) {\n+            super(namePart);\n+        }\n+        \n+        @Override\n+        public void validate() {\n+            if (partName.contains(\"://\")) { \n+                throw new DockerImageNameException(\"Invalid registry: Registry must not contain a schema\");\n+            }\n+        }\n+        \n+        @Override \n+        public boolean isValid() {\n+            return !partName.contains(\"://\");\n+        }\n+        \n+        @Override\n+        public RegistryName makeValid() {\n+            if (!isValid()) {\n+                removeSchema();\n+            }\n+            return this;\n+        }\n+        \n+        private void removeSchema() {\n+            int lastSchemaInstance = partName.lastIndexOf(\"://\");\n+            if (lastSchemaInstance > -1) {\n+                partName = partName.substring(lastSchemaInstance + 3);  // Remove the 3 schema chars too.\n+            }\n+        }\n+    }\n+    \n+    public static class NamespaceName extends AbstractDockerImageName {\n+        \n+        /**\n+         * Calls constructor on abstract to set namePart\n+         * @param namePart Part of image name\n+         */\n+        public NamespaceName(String namePart) {\n+            super(namePart);\n+            this.partRegexp = \"^([a-z0-9_-]*)$\";\n+            this.partInvertedRegexp = \"[^a-z0-9_-]\";\n+        }\n+        \n+        @Override\n+        public void validate() {\n+            if (partName.length() < 2 ||partName.length() > 255) {\n+                throw new DockerImageNameException(\"Invalid namespace: Namespace must be between 2 and 255 characters\");\n+            }\n+            if (partName.startsWith(\"-\") || partName.endsWith(\"-\")) {\n+                throw new DockerImageNameException(\"Invalid namespace: Namespace must not begin or end with a hyphon\");\n+            }\n+            if (partName.contains(\"--\")) {\n+                throw new DockerImageNameException(\"Invalid namespace: Namespace must not contain consecutive hyphons\");\n+            }\n+            if (!partName.matches(partRegexp)) {\n+                throw new DockerImageNameException(\"Invalid namespace: Namespace must match [a-z0-9_-]\");\n+            }\n+        }\n+        \n+        @Override\n+        public boolean isValid() {\n+            return partName.matches(partRegexp) && !partName.contains(\"--\") \n+                    && !partName.startsWith(\"-\") && !partName.endsWith(\"-\") && partName.length() < 255\n+                    && partName.length() > 2;\n+        }\n+        \n+        @Override\n+        public NamespaceName makeValid() {\n+            if (partName.length() > 255) {\n+                partName = partName.substring(0, 255);\n+            }\n+            if (!partName.matches(partRegexp)) {\n+                partName = spacesToHyphens(partName)\n+                        .toLowerCase()\n+                        .replaceAll(partInvertedRegexp, \"\");\n+            }\n+            while (partName.contains(\"--\")) {\n+                partName = partName.replaceAll(\"--\", \"-\");\n+            }\n+            partName = partName.replaceAll(\"^-\", \"\").replaceAll(\"-$\", \"\");\n+            while (partName.length() < 2) {\n+                partName += \"0\";\n+            }\n+            return this;\n+        }\n+    }\n+    \n+    public static class RepositoryName extends AbstractDockerImageName {\n+        \n+        /**\n+         * @param namePart Part of image name\n+         */\n+        public RepositoryName(String namePart) {\n+            super(namePart);\n+            partRegexp = \"^([a-z0-9_.-]*)$\";\n+            partInvertedRegexp = \"[^a-z0-9_.-]\";\n+        }\n+        \n+        @Override\n+        public void validate() {\n+            if (!partName.matches(partRegexp)) {\n+                throw new DockerImageNameException(\"Invalid registry: Registry must match [a-z0-9_.-]\");","path":"docker-plugin/src/main/java/com/nirima/jenkins/plugins/docker/utils/DockerImageName.java","position":433,"original_position":433,"commit_id":"7c44d5e2a68b8430d69acb95f7a005c1f5a39cd4","original_commit_id":"7c44d5e2a68b8430d69acb95f7a005c1f5a39cd4","user":{"login":"KostyaSha","id":231611,"avatar_url":"https://avatars.githubusercontent.com/u/231611?v=3","gravatar_id":"","url":"https://api.github.com/users/KostyaSha","html_url":"https://github.com/KostyaSha","followers_url":"https://api.github.com/users/KostyaSha/followers","following_url":"https://api.github.com/users/KostyaSha/following{/other_user}","gists_url":"https://api.github.com/users/KostyaSha/gists{/gist_id}","starred_url":"https://api.github.com/users/KostyaSha/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/KostyaSha/subscriptions","organizations_url":"https://api.github.com/users/KostyaSha/orgs","repos_url":"https://api.github.com/users/KostyaSha/repos","events_url":"https://api.github.com/users/KostyaSha/events{/privacy}","received_events_url":"https://api.github.com/users/KostyaSha/received_events","type":"User","site_admin":false},"body":"Why not just return boolean?","created_at":"2015-04-11T16:19:30Z","updated_at":"2015-04-11T16:19:30Z","html_url":"https://github.com/jenkinsci/docker-plugin/pull/202#discussion_r28196191","pull_request_url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202","_links":{"self":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/comments/28196191"},"html":{"href":"https://github.com/jenkinsci/docker-plugin/pull/202#discussion_r28196191"},"pull_request":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202"}}},"pull_request":{"url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202","id":33093284,"html_url":"https://github.com/jenkinsci/docker-plugin/pull/202","diff_url":"https://github.com/jenkinsci/docker-plugin/pull/202.diff","patch_url":"https://github.com/jenkinsci/docker-plugin/pull/202.patch","issue_url":"https://api.github.com/repos/jenkinsci/docker-plugin/issues/202","number":202,"state":"open","locked":false,"title":"[JENKINS-27900] Image Name class(es) and custom repository name","user":{"login":"jgriffiths1993","id":7528759,"avatar_url":"https://avatars.githubusercontent.com/u/7528759?v=3","gravatar_id":"","url":"https://api.github.com/users/jgriffiths1993","html_url":"https://github.com/jgriffiths1993","followers_url":"https://api.github.com/users/jgriffiths1993/followers","following_url":"https://api.github.com/users/jgriffiths1993/following{/other_user}","gists_url":"https://api.github.com/users/jgriffiths1993/gists{/gist_id}","starred_url":"https://api.github.com/users/jgriffiths1993/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgriffiths1993/subscriptions","organizations_url":"https://api.github.com/users/jgriffiths1993/orgs","repos_url":"https://api.github.com/users/jgriffiths1993/repos","events_url":"https://api.github.com/users/jgriffiths1993/events{/privacy}","received_events_url":"https://api.github.com/users/jgriffiths1993/received_events","type":"User","site_admin":false},"body":"* Custom repository name within optional block for jobProperty\r\n* jobProperty using DataBoundSetter for this field, as opposed to filling up DataBoundConstructor\r\n* DockerImageName class to provide an API for constructing, sanitizing and validation image names (and their respective components)\r\n* DockerImageNameException for identifying specific exceptions for this\r\n* Majority of logic and regular expressions for validation from Docker v1.5 source code\r\n* If repository is not specified, uses job name and sanitizes it properly\r\n* Sanitizes build number and additional tag\r\n* Fairly exhaustive tests for DockerImageName - see these for API\r\n\r\nPlease review; I'm sure that there will be some changes left to do!","created_at":"2015-04-11T00:09:02Z","updated_at":"2015-04-11T16:19:30Z","closed_at":null,"merged_at":null,"merge_commit_sha":"60682ab4915e43df95813d79818bec3b6d9fb098","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202/commits","review_comments_url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202/comments","review_comment_url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/comments{/number}","comments_url":"https://api.github.com/repos/jenkinsci/docker-plugin/issues/202/comments","statuses_url":"https://api.github.com/repos/jenkinsci/docker-plugin/statuses/7c44d5e2a68b8430d69acb95f7a005c1f5a39cd4","head":{"label":"jgriffiths1993:master","ref":"master","sha":"7c44d5e2a68b8430d69acb95f7a005c1f5a39cd4","user":{"login":"jgriffiths1993","id":7528759,"avatar_url":"https://avatars.githubusercontent.com/u/7528759?v=3","gravatar_id":"","url":"https://api.github.com/users/jgriffiths1993","html_url":"https://github.com/jgriffiths1993","followers_url":"https://api.github.com/users/jgriffiths1993/followers","following_url":"https://api.github.com/users/jgriffiths1993/following{/other_user}","gists_url":"https://api.github.com/users/jgriffiths1993/gists{/gist_id}","starred_url":"https://api.github.com/users/jgriffiths1993/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgriffiths1993/subscriptions","organizations_url":"https://api.github.com/users/jgriffiths1993/orgs","repos_url":"https://api.github.com/users/jgriffiths1993/repos","events_url":"https://api.github.com/users/jgriffiths1993/events{/privacy}","received_events_url":"https://api.github.com/users/jgriffiths1993/received_events","type":"User","site_admin":false},"repo":{"id":32644685,"name":"docker-plugin","full_name":"jgriffiths1993/docker-plugin","owner":{"login":"jgriffiths1993","id":7528759,"avatar_url":"https://avatars.githubusercontent.com/u/7528759?v=3","gravatar_id":"","url":"https://api.github.com/users/jgriffiths1993","html_url":"https://github.com/jgriffiths1993","followers_url":"https://api.github.com/users/jgriffiths1993/followers","following_url":"https://api.github.com/users/jgriffiths1993/following{/other_user}","gists_url":"https://api.github.com/users/jgriffiths1993/gists{/gist_id}","starred_url":"https://api.github.com/users/jgriffiths1993/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgriffiths1993/subscriptions","organizations_url":"https://api.github.com/users/jgriffiths1993/orgs","repos_url":"https://api.github.com/users/jgriffiths1993/repos","events_url":"https://api.github.com/users/jgriffiths1993/events{/privacy}","received_events_url":"https://api.github.com/users/jgriffiths1993/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jgriffiths1993/docker-plugin","description":"Jenkins Cloud Plugin for Docker","fork":true,"url":"https://api.github.com/repos/jgriffiths1993/docker-plugin","forks_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/forks","keys_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/teams","hooks_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/hooks","issue_events_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/issues/events{/number}","events_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/events","assignees_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/assignees{/user}","branches_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/branches{/branch}","tags_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/tags","blobs_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/git/refs{/sha}","trees_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/statuses/{sha}","languages_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/languages","stargazers_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/stargazers","contributors_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/contributors","subscribers_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/subscribers","subscription_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/subscription","commits_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/commits{/sha}","git_commits_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/git/commits{/sha}","comments_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/comments{/number}","issue_comment_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/issues/comments{/number}","contents_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/contents/{+path}","compare_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/merges","archive_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/downloads","issues_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/issues{/number}","pulls_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/pulls{/number}","milestones_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/milestones{/number}","notifications_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/labels{/name}","releases_url":"https://api.github.com/repos/jgriffiths1993/docker-plugin/releases{/id}","created_at":"2015-03-21T18:33:45Z","updated_at":"2015-04-11T14:21:24Z","pushed_at":"2015-04-11T14:21:24Z","git_url":"git://github.com/jgriffiths1993/docker-plugin.git","ssh_url":"git@github.com:jgriffiths1993/docker-plugin.git","clone_url":"https://github.com/jgriffiths1993/docker-plugin.git","svn_url":"https://github.com/jgriffiths1993/docker-plugin","homepage":"https://wiki.jenkins-ci.org/display/JENKINS/Docker+Plugin","size":761,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"jenkinsci:master","ref":"master","sha":"824c92a8ade027057a18d6a81e913ea2a1230b08","user":{"login":"jenkinsci","id":107424,"avatar_url":"https://avatars.githubusercontent.com/u/107424?v=3","gravatar_id":"","url":"https://api.github.com/users/jenkinsci","html_url":"https://github.com/jenkinsci","followers_url":"https://api.github.com/users/jenkinsci/followers","following_url":"https://api.github.com/users/jenkinsci/following{/other_user}","gists_url":"https://api.github.com/users/jenkinsci/gists{/gist_id}","starred_url":"https://api.github.com/users/jenkinsci/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jenkinsci/subscriptions","organizations_url":"https://api.github.com/users/jenkinsci/orgs","repos_url":"https://api.github.com/users/jenkinsci/repos","events_url":"https://api.github.com/users/jenkinsci/events{/privacy}","received_events_url":"https://api.github.com/users/jenkinsci/received_events","type":"Organization","site_admin":false},"repo":{"id":15799524,"name":"docker-plugin","full_name":"jenkinsci/docker-plugin","owner":{"login":"jenkinsci","id":107424,"avatar_url":"https://avatars.githubusercontent.com/u/107424?v=3","gravatar_id":"","url":"https://api.github.com/users/jenkinsci","html_url":"https://github.com/jenkinsci","followers_url":"https://api.github.com/users/jenkinsci/followers","following_url":"https://api.github.com/users/jenkinsci/following{/other_user}","gists_url":"https://api.github.com/users/jenkinsci/gists{/gist_id}","starred_url":"https://api.github.com/users/jenkinsci/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jenkinsci/subscriptions","organizations_url":"https://api.github.com/users/jenkinsci/orgs","repos_url":"https://api.github.com/users/jenkinsci/repos","events_url":"https://api.github.com/users/jenkinsci/events{/privacy}","received_events_url":"https://api.github.com/users/jenkinsci/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/jenkinsci/docker-plugin","description":"Jenkins Cloud Plugin for Docker","fork":false,"url":"https://api.github.com/repos/jenkinsci/docker-plugin","forks_url":"https://api.github.com/repos/jenkinsci/docker-plugin/forks","keys_url":"https://api.github.com/repos/jenkinsci/docker-plugin/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jenkinsci/docker-plugin/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jenkinsci/docker-plugin/teams","hooks_url":"https://api.github.com/repos/jenkinsci/docker-plugin/hooks","issue_events_url":"https://api.github.com/repos/jenkinsci/docker-plugin/issues/events{/number}","events_url":"https://api.github.com/repos/jenkinsci/docker-plugin/events","assignees_url":"https://api.github.com/repos/jenkinsci/docker-plugin/assignees{/user}","branches_url":"https://api.github.com/repos/jenkinsci/docker-plugin/branches{/branch}","tags_url":"https://api.github.com/repos/jenkinsci/docker-plugin/tags","blobs_url":"https://api.github.com/repos/jenkinsci/docker-plugin/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jenkinsci/docker-plugin/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jenkinsci/docker-plugin/git/refs{/sha}","trees_url":"https://api.github.com/repos/jenkinsci/docker-plugin/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jenkinsci/docker-plugin/statuses/{sha}","languages_url":"https://api.github.com/repos/jenkinsci/docker-plugin/languages","stargazers_url":"https://api.github.com/repos/jenkinsci/docker-plugin/stargazers","contributors_url":"https://api.github.com/repos/jenkinsci/docker-plugin/contributors","subscribers_url":"https://api.github.com/repos/jenkinsci/docker-plugin/subscribers","subscription_url":"https://api.github.com/repos/jenkinsci/docker-plugin/subscription","commits_url":"https://api.github.com/repos/jenkinsci/docker-plugin/commits{/sha}","git_commits_url":"https://api.github.com/repos/jenkinsci/docker-plugin/git/commits{/sha}","comments_url":"https://api.github.com/repos/jenkinsci/docker-plugin/comments{/number}","issue_comment_url":"https://api.github.com/repos/jenkinsci/docker-plugin/issues/comments{/number}","contents_url":"https://api.github.com/repos/jenkinsci/docker-plugin/contents/{+path}","compare_url":"https://api.github.com/repos/jenkinsci/docker-plugin/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jenkinsci/docker-plugin/merges","archive_url":"https://api.github.com/repos/jenkinsci/docker-plugin/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jenkinsci/docker-plugin/downloads","issues_url":"https://api.github.com/repos/jenkinsci/docker-plugin/issues{/number}","pulls_url":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls{/number}","milestones_url":"https://api.github.com/repos/jenkinsci/docker-plugin/milestones{/number}","notifications_url":"https://api.github.com/repos/jenkinsci/docker-plugin/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jenkinsci/docker-plugin/labels{/name}","releases_url":"https://api.github.com/repos/jenkinsci/docker-plugin/releases{/id}","created_at":"2014-01-10T14:05:42Z","updated_at":"2015-04-11T10:16:03Z","pushed_at":"2015-04-11T14:21:24Z","git_url":"git://github.com/jenkinsci/docker-plugin.git","ssh_url":"git@github.com:jenkinsci/docker-plugin.git","clone_url":"https://github.com/jenkinsci/docker-plugin.git","svn_url":"https://github.com/jenkinsci/docker-plugin","homepage":"https://wiki.jenkins-ci.org/display/JENKINS/Docker+Plugin","size":1970,"stargazers_count":139,"watchers_count":139,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":93,"mirror_url":null,"open_issues_count":67,"forks":93,"open_issues":67,"watchers":139,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202"},"html":{"href":"https://github.com/jenkinsci/docker-plugin/pull/202"},"issue":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/issues/202"},"comments":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/issues/202/comments"},"review_comments":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202/comments"},"review_comment":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/pulls/202/commits"},"statuses":{"href":"https://api.github.com/repos/jenkinsci/docker-plugin/statuses/7c44d5e2a68b8430d69acb95f7a005c1f5a39cd4"}}}}