{"action":"created","comment":{"url":"https://api.github.com/repos/openshift/ci-operator/pulls/comments/219443734","pull_request_review_id":157620814,"id":219443734,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxOTQ0MzczNA==","diff_hunk":"@@ -3,71 +3,177 @@ package api\n import (\n \t\"errors\"\n \t\"fmt\"\n+\t\"net/url\"\n+\t\"sort\"\n+\t\"strings\"\n \n \tkerrors \"k8s.io/apimachinery/pkg/util/errors\"\n )\n \n-// Validate validates config\n+// Validate validates all the configuration's values.\n func (config *ReleaseBuildConfiguration) Validate() error {\n-\terrs := []error{}\n+\tvar validationErrors []error\n+\n+\tif config.InputConfiguration.BuildRootImage != nil {\n+\t\tvalidationErrors = append(validationErrors, validateBuildRootImageConfiguration(\"build_root\", *config.InputConfiguration.BuildRootImage))\n+\t} else {\n+\t\tvalidationErrors = append(validationErrors, errors.New(\"build_root is required\"))\n+\t}\n+\n \tif config.Tests != nil {\n-\t\tfor _, test := range config.Tests {\n-\t\t\tif test.As == \"images\" {\n-\t\t\t\terrs = append(errs, errors.New(\"test should not be called 'images' because it gets confused with '[images]' target\"))\n-\t\t\t}\n-\t\t\ttypeCount := 0\n-\t\t\tif testConfig := test.ContainerTestConfiguration; testConfig != nil {\n-\t\t\t\ttypeCount += 1\n-\t\t\t\t// TODO remove when the migration is completed\n-\t\t\t\tif len(testConfig.From) == 0 && len(test.From) == 0 {\n-\t\t\t\t\terrs = append(errs, fmt.Errorf(\"test %q: `from` is required\", test.As))\n-\t\t\t\t}\n-\t\t\t}\n-\t\t\tif testConfig := test.OpenshiftAnsibleClusterTestConfiguration; testConfig != nil {\n-\t\t\t\ttypeCount += 1\n-\t\t\t\terrs = append(errs, validateTargetCloud(test.As, testConfig.TargetCloud))\n-\t\t\t}\n-\t\t\tif testConfig := test.OpenshiftAnsibleSrcClusterTestConfiguration; testConfig != nil {\n-\t\t\t\ttypeCount += 1\n-\t\t\t\terrs = append(errs, validateTargetCloud(test.As, testConfig.TargetCloud))\n-\t\t\t}\n-\t\t\tif testConfig := test.OpenshiftInstallerClusterTestConfiguration; testConfig != nil {\n-\t\t\t\ttypeCount += 1\n-\t\t\t\terrs = append(errs, validateTargetCloud(test.As, testConfig.TargetCloud))\n+\t\tvalidationErrors = append(validationErrors, validateTestStepConfiguration(\"tests\", config.Tests))\n+\t}\n+\n+\tif config.InputConfiguration.BaseImages != nil {\n+\t\tvalidationErrors = append(validationErrors, validateImageStreamTagReferenceMap(\"base_images\", config.InputConfiguration.BaseImages))\n+\t}\n+\n+\tif config.InputConfiguration.BaseRPMImages != nil {\n+\t\tvalidationErrors = append(validationErrors, validateImageStreamTagReferenceMap(\"base_rpm_images\", config.InputConfiguration.BaseRPMImages))\n+\t}\n+\n+\tif config.PromotionConfiguration != nil {\n+\t\tvalidationErrors = append(validationErrors, validatePromotionConfiguration(\"promotion\", *config.PromotionConfiguration))\n+\t}\n+\n+\tif config.InputConfiguration.ReleaseTagConfiguration != nil {\n+\t\tvalidationErrors = append(validationErrors, validateReleaseTagConfiguration(\"tag_specification\", *config.InputConfiguration.ReleaseTagConfiguration))\n+\t}\n+\n+\treturn kerrors.NewAggregate(validationErrors)\n+}\n+\n+func validateBuildRootImageConfiguration(fieldRoot string, input BuildRootImageConfiguration) error {\n+\tif input.ProjectImageBuild != nil && input.ImageStreamTagReference != nil {\n+\t\treturn fmt.Errorf(\"%s: both image_stream_tag and project_image_build cannot be set\", fieldRoot)\n+\t} else if input.ProjectImageBuild == nil && input.ImageStreamTagReference == nil {\n+\t\treturn fmt.Errorf(\"%s: you have to specify either image_stream_tag or project_image_build\", fieldRoot)\n+\t}\n+\treturn nil\n+}\n+\n+func validateTestStepConfiguration(fieldRoot string, input []TestStepConfiguration) error {\n+\tvar validationErrors []error\n+\ttypeCount := 0\n+\n+\t// check for test.As duplicates\n+\tvalidationErrors = append(validationErrors, searchForTestDuplicates(input))\n+\n+\tfor num, test := range input {\n+\t\tif len(test.As) == 0 {\n+\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d].as: is required\", fieldRoot, num))\n+\t\t} else if test.As == \"images\" {\n+\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d].as: should not be called 'images' because it gets confused with '[images]' target\", fieldRoot, num))\n+\t\t}\n+\n+\t\tif len(test.Commands) == 0 {\n+\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d].commands: is required\", fieldRoot, num))\n+\t\t}\n+\n+\t\tif testConfig := test.ContainerTestConfiguration; testConfig != nil {\n+\t\t\ttypeCount++\n+\t\t\t// TODO remove when the migration is completed\n+\t\t\tif len(testConfig.From) == 0 && len(test.From) == 0 {\n+\t\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d]: `from` is required\", fieldRoot, num))\n \t\t\t}\n-\t\t\tif testConfig := test.OpenshiftInstallerSmokeClusterTestConfiguration; testConfig != nil {\n-\t\t\t\ttypeCount += 1\n-\t\t\t\terrs = append(errs, validateTargetCloud(test.As, testConfig.TargetCloud))\n+\t\t}\n+\t\tif testConfig := test.OpenshiftAnsibleClusterTestConfiguration; testConfig != nil {\n+\t\t\ttypeCount++\n+\t\t\tvalidationErrors = append(validationErrors, validateTargetCloud(fmt.Sprintf(\"%s[%d]\", fieldRoot, num), testConfig.TargetCloud))\n+\t\t}\n+\t\tif testConfig := test.OpenshiftAnsibleSrcClusterTestConfiguration; testConfig != nil {\n+\t\t\ttypeCount++\n+\t\t\tvalidationErrors = append(validationErrors, validateTargetCloud(fmt.Sprintf(\"%s[%d]\", fieldRoot, num), testConfig.TargetCloud))\n+\t\t}\n+\t\tif testConfig := test.OpenshiftInstallerClusterTestConfiguration; testConfig != nil {\n+\t\t\ttypeCount++\n+\t\t\tvalidationErrors = append(validationErrors, validateTargetCloud(fmt.Sprintf(\"%s[%d]\", fieldRoot, num), testConfig.TargetCloud))\n+\t\t}\n+\t\tif testConfig := test.OpenshiftInstallerSmokeClusterTestConfiguration; testConfig != nil {\n+\t\t\ttypeCount++\n+\t\t\tvalidationErrors = append(validationErrors, validateTargetCloud(fmt.Sprintf(\"%s[%d]\", fieldRoot, num), testConfig.TargetCloud))\n+\t\t}\n+\t\tif typeCount == 0 {\n+\t\t\t// TODO remove when the migration is completed\n+\t\t\tif len(test.From) == 0 {\n+\t\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d] has no type\", fieldRoot, num))\n \t\t\t}\n-\t\t\tif typeCount == 0 {\n-\t\t\t\t// TODO remove when the migration is completed\n-\t\t\t\tif len(test.From) == 0 {\n-\t\t\t\t\terrs = append(errs, fmt.Errorf(\"test %q has no type\", test.As))\n-\t\t\t\t}\n-\t\t\t} else if typeCount == 1 {\n-\t\t\t\t// TODO remove when the migration is completed\n-\t\t\t\tif len(test.From) > 0 {\n-\t\t\t\t\terrs = append(errs, fmt.Errorf(\"test %q specifies both `From` and a test type\", test.As))\n-\t\t\t\t}\n-\t\t\t} else if typeCount > 1 {\n-\t\t\t\terrs = append(errs, fmt.Errorf(\"test %q has more than one type\", test.As))\n+\t\t} else if typeCount == 1 {\n+\t\t\t// TODO remove when the migration is completed\n+\t\t\tif len(test.From) > 0 {\n+\t\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d] specifies both `From` and a test type\", fieldRoot, num))\n \t\t\t}\n+\t\t} else if typeCount > 1 {\n+\t\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s[%d] has more than one type\", fieldRoot, num))\n \t\t}\n \t}\n-\tbuildRootImage := config.InputConfiguration.BuildRootImage\n-\tif buildRootImage != nil {\n-\t\tif buildRootImage.ProjectImageBuild != nil && buildRootImage.ImageStreamTagReference != nil {\n-\t\t\terrs = append(errs, errors.New(\"both git_source_image and image_stream_tag cannot be set for the build_root\"))\n-\t\t} else if buildRootImage.ProjectImageBuild == nil && buildRootImage.ImageStreamTagReference == nil {\n-\t\t\terrs = append(errs, errors.New(\"you have to specify either git_source_image or image_stream_tag for the build_root\"))\n-\t\t}\n+\treturn kerrors.NewAggregate(validationErrors)\n+}\n+\n+func validateImageStreamTagReference(fieldRoot string, input ImageStreamTagReference) error {\n+\tvar validationErrors []error\n+\n+\tif _, err := url.ParseRequestURI(input.Cluster); err != nil && input.Cluster != \"\" {\n+\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s.cluster invalid URL given: %s\", fieldRoot, err))\n+\t}\n+\n+\tif input.Tag == \"\" {\n+\t\tvalidationErrors = append(validationErrors, fmt.Errorf(\"%s.tag: value required but not provided\", fieldRoot))\n+\t}\n+\n+\treturn kerrors.NewAggregate(validationErrors)\n+}\n+\n+func validateImageStreamTagReferenceMap(fieldRoot string, input map[string]ImageStreamTagReference) error {\n+\tvar validationErrors []error\n+\t// map has to be sorted to avoid flakes in tests.","path":"pkg/api/config.go","position":174,"original_position":174,"commit_id":"e5443d697f745817631282963a8159d86600df53","original_commit_id":"e5443d697f745817631282963a8159d86600df53","user":{"login":"droslean","id":8650912,"node_id":"MDQ6VXNlcjg2NTA5MTI=","avatar_url":"https://avatars0.githubusercontent.com/u/8650912?v=4","gravatar_id":"","url":"https://api.github.com/users/droslean","html_url":"https://github.com/droslean","followers_url":"https://api.github.com/users/droslean/followers","following_url":"https://api.github.com/users/droslean/following{/other_user}","gists_url":"https://api.github.com/users/droslean/gists{/gist_id}","starred_url":"https://api.github.com/users/droslean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droslean/subscriptions","organizations_url":"https://api.github.com/users/droslean/orgs","repos_url":"https://api.github.com/users/droslean/repos","events_url":"https://api.github.com/users/droslean/events{/privacy}","received_events_url":"https://api.github.com/users/droslean/received_events","type":"User","site_admin":false},"body":"No, it can be removed. The errorMessage have been removed [here](https://github.com/openshift/ci-operator/commit/ef19d3ab9d5cad8f2199873b85d112a44e2b2aa7#diff-787bc42cef1afcb577450319f808c3b2L12)","created_at":"2018-09-21T09:51:29Z","updated_at":"2018-09-21T09:51:29Z","html_url":"https://github.com/openshift/ci-operator/pull/143#discussion_r219443734","pull_request_url":"https://api.github.com/repos/openshift/ci-operator/pulls/143","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/openshift/ci-operator/pulls/comments/219443734"},"html":{"href":"https://github.com/openshift/ci-operator/pull/143#discussion_r219443734"},"pull_request":{"href":"https://api.github.com/repos/openshift/ci-operator/pulls/143"}},"in_reply_to_id":219263187},"pull_request":{"url":"https://api.github.com/repos/openshift/ci-operator/pulls/143","id":215631275,"node_id":"MDExOlB1bGxSZXF1ZXN0MjE1NjMxMjc1","html_url":"https://github.com/openshift/ci-operator/pull/143","diff_url":"https://github.com/openshift/ci-operator/pull/143.diff","patch_url":"https://github.com/openshift/ci-operator/pull/143.patch","issue_url":"https://api.github.com/repos/openshift/ci-operator/issues/143","number":143,"state":"open","locked":false,"title":"Config validation updates.","user":{"login":"droslean","id":8650912,"node_id":"MDQ6VXNlcjg2NTA5MTI=","avatar_url":"https://avatars0.githubusercontent.com/u/8650912?v=4","gravatar_id":"","url":"https://api.github.com/users/droslean","html_url":"https://github.com/droslean","followers_url":"https://api.github.com/users/droslean/followers","following_url":"https://api.github.com/users/droslean/following{/other_user}","gists_url":"https://api.github.com/users/droslean/gists{/gist_id}","starred_url":"https://api.github.com/users/droslean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droslean/subscriptions","organizations_url":"https://api.github.com/users/droslean/orgs","repos_url":"https://api.github.com/users/droslean/repos","events_url":"https://api.github.com/users/droslean/events{/privacy}","received_events_url":"https://api.github.com/users/droslean/received_events","type":"User","site_admin":false},"body":"Add a validator for the test configuration files.\r\n\r\n#### TODOs\r\n- [X] Validate build_root\r\n- [X] Validate base_images\r\n- [X] Validate base_rpm_images\r\n- [X] Validate tests\r\n- [X] Validate promotion\r\n- [X] Validate tag_specification\r\n- [X] Tests cases\r\n- [X] Check for Tests.As duplicates.","created_at":"2018-09-14T15:28:28Z","updated_at":"2018-09-21T09:51:29Z","closed_at":null,"merged_at":null,"merge_commit_sha":"40d00cb0807bbf60888eb90ba430a7b1516d0ace","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":912759997,"node_id":"MDU6TGFiZWw5MTI3NTk5OTc=","url":"https://api.github.com/repos/openshift/ci-operator/labels/size/L","name":"size/L","color":"ee9900","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/openshift/ci-operator/pulls/143/commits","review_comments_url":"https://api.github.com/repos/openshift/ci-operator/pulls/143/comments","review_comment_url":"https://api.github.com/repos/openshift/ci-operator/pulls/comments{/number}","comments_url":"https://api.github.com/repos/openshift/ci-operator/issues/143/comments","statuses_url":"https://api.github.com/repos/openshift/ci-operator/statuses/e5443d697f745817631282963a8159d86600df53","head":{"label":"droslean:validate","ref":"validate","sha":"e5443d697f745817631282963a8159d86600df53","user":{"login":"droslean","id":8650912,"node_id":"MDQ6VXNlcjg2NTA5MTI=","avatar_url":"https://avatars0.githubusercontent.com/u/8650912?v=4","gravatar_id":"","url":"https://api.github.com/users/droslean","html_url":"https://github.com/droslean","followers_url":"https://api.github.com/users/droslean/followers","following_url":"https://api.github.com/users/droslean/following{/other_user}","gists_url":"https://api.github.com/users/droslean/gists{/gist_id}","starred_url":"https://api.github.com/users/droslean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droslean/subscriptions","organizations_url":"https://api.github.com/users/droslean/orgs","repos_url":"https://api.github.com/users/droslean/repos","events_url":"https://api.github.com/users/droslean/events{/privacy}","received_events_url":"https://api.github.com/users/droslean/received_events","type":"User","site_admin":false},"repo":{"id":143710840,"node_id":"MDEwOlJlcG9zaXRvcnkxNDM3MTA4NDA=","name":"ci-operator","full_name":"droslean/ci-operator","private":false,"owner":{"login":"droslean","id":8650912,"node_id":"MDQ6VXNlcjg2NTA5MTI=","avatar_url":"https://avatars0.githubusercontent.com/u/8650912?v=4","gravatar_id":"","url":"https://api.github.com/users/droslean","html_url":"https://github.com/droslean","followers_url":"https://api.github.com/users/droslean/followers","following_url":"https://api.github.com/users/droslean/following{/other_user}","gists_url":"https://api.github.com/users/droslean/gists{/gist_id}","starred_url":"https://api.github.com/users/droslean/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/droslean/subscriptions","organizations_url":"https://api.github.com/users/droslean/orgs","repos_url":"https://api.github.com/users/droslean/repos","events_url":"https://api.github.com/users/droslean/events{/privacy}","received_events_url":"https://api.github.com/users/droslean/received_events","type":"User","site_admin":false},"html_url":"https://github.com/droslean/ci-operator","description":null,"fork":true,"url":"https://api.github.com/repos/droslean/ci-operator","forks_url":"https://api.github.com/repos/droslean/ci-operator/forks","keys_url":"https://api.github.com/repos/droslean/ci-operator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/droslean/ci-operator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/droslean/ci-operator/teams","hooks_url":"https://api.github.com/repos/droslean/ci-operator/hooks","issue_events_url":"https://api.github.com/repos/droslean/ci-operator/issues/events{/number}","events_url":"https://api.github.com/repos/droslean/ci-operator/events","assignees_url":"https://api.github.com/repos/droslean/ci-operator/assignees{/user}","branches_url":"https://api.github.com/repos/droslean/ci-operator/branches{/branch}","tags_url":"https://api.github.com/repos/droslean/ci-operator/tags","blobs_url":"https://api.github.com/repos/droslean/ci-operator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/droslean/ci-operator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/droslean/ci-operator/git/refs{/sha}","trees_url":"https://api.github.com/repos/droslean/ci-operator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/droslean/ci-operator/statuses/{sha}","languages_url":"https://api.github.com/repos/droslean/ci-operator/languages","stargazers_url":"https://api.github.com/repos/droslean/ci-operator/stargazers","contributors_url":"https://api.github.com/repos/droslean/ci-operator/contributors","subscribers_url":"https://api.github.com/repos/droslean/ci-operator/subscribers","subscription_url":"https://api.github.com/repos/droslean/ci-operator/subscription","commits_url":"https://api.github.com/repos/droslean/ci-operator/commits{/sha}","git_commits_url":"https://api.github.com/repos/droslean/ci-operator/git/commits{/sha}","comments_url":"https://api.github.com/repos/droslean/ci-operator/comments{/number}","issue_comment_url":"https://api.github.com/repos/droslean/ci-operator/issues/comments{/number}","contents_url":"https://api.github.com/repos/droslean/ci-operator/contents/{+path}","compare_url":"https://api.github.com/repos/droslean/ci-operator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/droslean/ci-operator/merges","archive_url":"https://api.github.com/repos/droslean/ci-operator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/droslean/ci-operator/downloads","issues_url":"https://api.github.com/repos/droslean/ci-operator/issues{/number}","pulls_url":"https://api.github.com/repos/droslean/ci-operator/pulls{/number}","milestones_url":"https://api.github.com/repos/droslean/ci-operator/milestones{/number}","notifications_url":"https://api.github.com/repos/droslean/ci-operator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/droslean/ci-operator/labels{/name}","releases_url":"https://api.github.com/repos/droslean/ci-operator/releases{/id}","deployments_url":"https://api.github.com/repos/droslean/ci-operator/deployments","created_at":"2018-08-06T10:07:21Z","updated_at":"2018-09-13T10:11:30Z","pushed_at":"2018-09-20T11:29:38Z","git_url":"git://github.com/droslean/ci-operator.git","ssh_url":"git@github.com:droslean/ci-operator.git","clone_url":"https://github.com/droslean/ci-operator.git","svn_url":"https://github.com/droslean/ci-operator","homepage":null,"size":14161,"stargazers_count":0,"watchers_count":0,"language":"Go","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"openshift:master","ref":"master","sha":"c5be264ac8a7577263a68a24ec6740acf85f47f1","user":{"login":"openshift","id":792337,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc5MjMzNw==","avatar_url":"https://avatars3.githubusercontent.com/u/792337?v=4","gravatar_id":"","url":"https://api.github.com/users/openshift","html_url":"https://github.com/openshift","followers_url":"https://api.github.com/users/openshift/followers","following_url":"https://api.github.com/users/openshift/following{/other_user}","gists_url":"https://api.github.com/users/openshift/gists{/gist_id}","starred_url":"https://api.github.com/users/openshift/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openshift/subscriptions","organizations_url":"https://api.github.com/users/openshift/orgs","repos_url":"https://api.github.com/users/openshift/repos","events_url":"https://api.github.com/users/openshift/events{/privacy}","received_events_url":"https://api.github.com/users/openshift/received_events","type":"Organization","site_admin":false},"repo":{"id":119440554,"node_id":"MDEwOlJlcG9zaXRvcnkxMTk0NDA1NTQ=","name":"ci-operator","full_name":"openshift/ci-operator","private":false,"owner":{"login":"openshift","id":792337,"node_id":"MDEyOk9yZ2FuaXphdGlvbjc5MjMzNw==","avatar_url":"https://avatars3.githubusercontent.com/u/792337?v=4","gravatar_id":"","url":"https://api.github.com/users/openshift","html_url":"https://github.com/openshift","followers_url":"https://api.github.com/users/openshift/followers","following_url":"https://api.github.com/users/openshift/following{/other_user}","gists_url":"https://api.github.com/users/openshift/gists{/gist_id}","starred_url":"https://api.github.com/users/openshift/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/openshift/subscriptions","organizations_url":"https://api.github.com/users/openshift/orgs","repos_url":"https://api.github.com/users/openshift/repos","events_url":"https://api.github.com/users/openshift/events{/privacy}","received_events_url":"https://api.github.com/users/openshift/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/openshift/ci-operator","description":null,"fork":false,"url":"https://api.github.com/repos/openshift/ci-operator","forks_url":"https://api.github.com/repos/openshift/ci-operator/forks","keys_url":"https://api.github.com/repos/openshift/ci-operator/keys{/key_id}","collaborators_url":"https://api.github.com/repos/openshift/ci-operator/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/openshift/ci-operator/teams","hooks_url":"https://api.github.com/repos/openshift/ci-operator/hooks","issue_events_url":"https://api.github.com/repos/openshift/ci-operator/issues/events{/number}","events_url":"https://api.github.com/repos/openshift/ci-operator/events","assignees_url":"https://api.github.com/repos/openshift/ci-operator/assignees{/user}","branches_url":"https://api.github.com/repos/openshift/ci-operator/branches{/branch}","tags_url":"https://api.github.com/repos/openshift/ci-operator/tags","blobs_url":"https://api.github.com/repos/openshift/ci-operator/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/openshift/ci-operator/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/openshift/ci-operator/git/refs{/sha}","trees_url":"https://api.github.com/repos/openshift/ci-operator/git/trees{/sha}","statuses_url":"https://api.github.com/repos/openshift/ci-operator/statuses/{sha}","languages_url":"https://api.github.com/repos/openshift/ci-operator/languages","stargazers_url":"https://api.github.com/repos/openshift/ci-operator/stargazers","contributors_url":"https://api.github.com/repos/openshift/ci-operator/contributors","subscribers_url":"https://api.github.com/repos/openshift/ci-operator/subscribers","subscription_url":"https://api.github.com/repos/openshift/ci-operator/subscription","commits_url":"https://api.github.com/repos/openshift/ci-operator/commits{/sha}","git_commits_url":"https://api.github.com/repos/openshift/ci-operator/git/commits{/sha}","comments_url":"https://api.github.com/repos/openshift/ci-operator/comments{/number}","issue_comment_url":"https://api.github.com/repos/openshift/ci-operator/issues/comments{/number}","contents_url":"https://api.github.com/repos/openshift/ci-operator/contents/{+path}","compare_url":"https://api.github.com/repos/openshift/ci-operator/compare/{base}...{head}","merges_url":"https://api.github.com/repos/openshift/ci-operator/merges","archive_url":"https://api.github.com/repos/openshift/ci-operator/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/openshift/ci-operator/downloads","issues_url":"https://api.github.com/repos/openshift/ci-operator/issues{/number}","pulls_url":"https://api.github.com/repos/openshift/ci-operator/pulls{/number}","milestones_url":"https://api.github.com/repos/openshift/ci-operator/milestones{/number}","notifications_url":"https://api.github.com/repos/openshift/ci-operator/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/openshift/ci-operator/labels{/name}","releases_url":"https://api.github.com/repos/openshift/ci-operator/releases{/id}","deployments_url":"https://api.github.com/repos/openshift/ci-operator/deployments","created_at":"2018-01-29T20:55:49Z","updated_at":"2018-09-20T23:12:45Z","pushed_at":"2018-09-20T23:12:42Z","git_url":"git://github.com/openshift/ci-operator.git","ssh_url":"git@github.com:openshift/ci-operator.git","clone_url":"https://github.com/openshift/ci-operator.git","svn_url":"https://github.com/openshift/ci-operator","homepage":null,"size":14145,"stargazers_count":15,"watchers_count":15,"language":"Go","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":20,"mirror_url":null,"archived":false,"open_issues_count":8,"license":null,"forks":20,"open_issues":8,"watchers":15,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/openshift/ci-operator/pulls/143"},"html":{"href":"https://github.com/openshift/ci-operator/pull/143"},"issue":{"href":"https://api.github.com/repos/openshift/ci-operator/issues/143"},"comments":{"href":"https://api.github.com/repos/openshift/ci-operator/issues/143/comments"},"review_comments":{"href":"https://api.github.com/repos/openshift/ci-operator/pulls/143/comments"},"review_comment":{"href":"https://api.github.com/repos/openshift/ci-operator/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/openshift/ci-operator/pulls/143/commits"},"statuses":{"href":"https://api.github.com/repos/openshift/ci-operator/statuses/e5443d697f745817631282963a8159d86600df53"}},"author_association":"MEMBER"}}
{"actor":{"display_login":"droslean"}}