From 9468bfa5d756a5948b2a271f310161e3382a9e7b Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Wed, 22 Feb 2017 16:46:02 -0600 Subject: [PATCH 1/9] list matching jobs using dsl. --- lib/jenkins_api_client/job.rb | 62 +++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/lib/jenkins_api_client/job.rb b/lib/jenkins_api_client/job.rb index ef7cd751..3d0be311 100644 --- a/lib/jenkins_api_client/job.rb +++ b/lib/jenkins_api_client/job.rb @@ -653,13 +653,63 @@ def list_by_status(status, jobs = []) # def list(filter, ignorecase = true) @logger.info "Obtaining jobs matching filter '#{filter}'" - response_json = @client.api_get_request("") + text = @client.exec_script(<<'EOS') + def Map findJobs(Object obj, String namespace = null) { + def found = [:] + + // groovy apparently can't #collect on a list and return a map? + obj.items.each { job -> + // a possibly better approach would be to walk the parent chain from // + // each job + def path = job.getName() + if (namespace) { + path = "${namespace}/" + path + } + found[path] = job + // intentionally not using `instanceof` here so we don't blow up if the + // cloudbees-folder plugin is not installed + if (job.getClass().getName() == 'com.cloudbees.hudson.plugins.folder.Folder') { + found << findJobs(job, path) + } + } + + found + } + + void job_list_json() { + def jobs = findJobs(Jenkins.getInstance()) + + def allInfo = jobs.collect { path, job -> + // at least these job classes do not respond to respond to #isDisabled: + // - org.jenkinsci.plugins.workflow.job.WorkflowJob + // - com.cloudbees.hudson.plugins.folder.Folder + def enabled = false + if (job.metaClass.respondsTo(job, 'isDisabled')) { + enabled = !job.isDisabled() + } + + [ + _class: job.getClass().toString(), + name: path, + ] + } + + def builder = new groovy.json.JsonBuilder(allInfo) + out.println(builder.toPrettyString()) + } + + job_list_json() +EOS + response_json = JSON.parse(text) + jobs = [] - response_json["jobs"].each do |job| - if ignorecase - jobs << job["name"] if job["name"] =~ /#{filter}/i - else - jobs << job["name"] if job["name"] =~ /#{filter}/ + response_json.each do |job| + if job["_class"] != "class com.cloudbees.hudson.plugins.folder.Folder" + if ignorecase + jobs << job["name"] if job["name"] =~ /#{filter}/i + else + jobs << job["name"] if job["name"] =~ /#{filter}/ + end end end jobs From 82c78ce7528b3110622b88915d96009092ac5e1c Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Fri, 24 Feb 2017 09:56:54 -0600 Subject: [PATCH 2/9] install an older version of rake. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index dd139ff8..28651c96 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,7 @@ group :development do gem "yard-thor" gem "yard" gem "pry" - gem "rake" + gem "rake", "< 12.0" gem "jeweler" gem "rack", "~> 1.0" end From 9c03cc511bc27412916505ff34d938af2ad771c4 Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Fri, 24 Feb 2017 17:00:44 -0600 Subject: [PATCH 3/9] pinning more versions for ruby 1.9 support? is this still really required? --- Gemfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 28651c96..02c3d28c 100644 --- a/Gemfile +++ b/Gemfile @@ -10,11 +10,11 @@ gem 'json', '~> 1.0' group :development do gem "bundler", ">= 1.0" gem "rspec", "~> 2.14.1" - gem "simplecov" + gem "simplecov", "0.10.0" gem "yard-thor" gem "yard" gem "pry" gem "rake", "< 12.0" - gem "jeweler" + gem "jeweler", "< 2.3.3" gem "rack", "~> 1.0" end From e65104a1d8c308d2a8fe6e63cb9d8aa8fcb5edba Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Mon, 3 Apr 2017 11:56:41 -0500 Subject: [PATCH 4/9] output more similar to output from api_get_request --- lib/jenkins_api_client/job.rb | 53 +++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/jenkins_api_client/job.rb b/lib/jenkins_api_client/job.rb index 3d0be311..7d1c85de 100644 --- a/lib/jenkins_api_client/job.rb +++ b/lib/jenkins_api_client/job.rb @@ -700,10 +700,59 @@ def builder = new groovy.json.JsonBuilder(allInfo) job_list_json() EOS - response_json = JSON.parse(text) + groovy_script_output = @client.exec_script(<<'EOS') + def Map findJobs(Object obj, String namespace = null) { + def found = [:] + + // groovy apparently can't #collect on a list and return a map? + obj.items.each { job -> + // a possibly better approach would be to walk the parent chain from // + // each job + def path = job.getName() + if (namespace) { + path = "${namespace}/" + path + } + found[path] = job + // intentionally not using `instanceof` here so we don't blow up if the + // cloudbees-folder plugin is not installed + if (job.getClass().getName() == 'com.cloudbees.hudson.plugins.folder.Folder') { + found << findJobs(job, path) + } + } + + found + } + + void job_list_json() { + def jobs = findJobs(Jenkins.getInstance()) + + def allInfo = jobs.collect { path, job -> + // at least these job classes do not respond to respond to #isDisabled: + // - org.jenkinsci.plugins.workflow.job.WorkflowJob + // - com.cloudbees.hudson.plugins.folder.Folder + def enabled = false + if (job.metaClass.respondsTo(job, 'isDisabled')) { + enabled = !job.isDisabled() + } + + [ + _class: job.getClass().toString(), + name: path, + url: Hudson.getInstance().getRootUrl().toString() + job.getUrl().toString(), + ] + } + + def builder = new groovy.json.JsonBuilder(allInfo) + out.println(builder.toPrettyString()) + } + + job_list_json() +EOS + response_json = JSON.parse(groovy_script_output) + response_json = { "jobs" => response_json } jobs = [] - response_json.each do |job| + response_json["jobs"].each do |job| if job["_class"] != "class com.cloudbees.hudson.plugins.folder.Folder" if ignorecase jobs << job["name"] if job["name"] =~ /#{filter}/i From ad82957ae9aa62cf7d82d13c187d8866f347a302 Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Mon, 3 Apr 2017 14:12:00 -0500 Subject: [PATCH 5/9] updated to remove duplicate script. --- lib/jenkins_api_client/job.rb | 53 ++--------------------------------- 1 file changed, 3 insertions(+), 50 deletions(-) diff --git a/lib/jenkins_api_client/job.rb b/lib/jenkins_api_client/job.rb index 7d1c85de..0d437f86 100644 --- a/lib/jenkins_api_client/job.rb +++ b/lib/jenkins_api_client/job.rb @@ -653,55 +653,8 @@ def list_by_status(status, jobs = []) # def list(filter, ignorecase = true) @logger.info "Obtaining jobs matching filter '#{filter}'" - text = @client.exec_script(<<'EOS') - def Map findJobs(Object obj, String namespace = null) { - def found = [:] - - // groovy apparently can't #collect on a list and return a map? - obj.items.each { job -> - // a possibly better approach would be to walk the parent chain from // - // each job - def path = job.getName() - if (namespace) { - path = "${namespace}/" + path - } - found[path] = job - // intentionally not using `instanceof` here so we don't blow up if the - // cloudbees-folder plugin is not installed - if (job.getClass().getName() == 'com.cloudbees.hudson.plugins.folder.Folder') { - found << findJobs(job, path) - } - } - - found - } - - void job_list_json() { - def jobs = findJobs(Jenkins.getInstance()) - - def allInfo = jobs.collect { path, job -> - // at least these job classes do not respond to respond to #isDisabled: - // - org.jenkinsci.plugins.workflow.job.WorkflowJob - // - com.cloudbees.hudson.plugins.folder.Folder - def enabled = false - if (job.metaClass.respondsTo(job, 'isDisabled')) { - enabled = !job.isDisabled() - } - - [ - _class: job.getClass().toString(), - name: path, - ] - } - - def builder = new groovy.json.JsonBuilder(allInfo) - out.println(builder.toPrettyString()) - } - - job_list_json() -EOS groovy_script_output = @client.exec_script(<<'EOS') - def Map findJobs(Object obj, String namespace = null) { + def Map findJobs(Object obj, String namespace = null) { def found = [:] // groovy apparently can't #collect on a list and return a map? @@ -736,7 +689,7 @@ def enabled = false } [ - _class: job.getClass().toString(), + _class: job.getClass().getCanonicalName(), name: path, url: Hudson.getInstance().getRootUrl().toString() + job.getUrl().toString(), ] @@ -753,7 +706,7 @@ def builder = new groovy.json.JsonBuilder(allInfo) jobs = [] response_json["jobs"].each do |job| - if job["_class"] != "class com.cloudbees.hudson.plugins.folder.Folder" + if job["_class"] !~ /com.cloudbees.hudson.plugins.folder.Folder/ if ignorecase jobs << job["name"] if job["name"] =~ /#{filter}/i else From d03d141c3d17f539fc2434b7a6c11b96d69c8311 Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Mon, 3 Apr 2017 15:02:54 -0500 Subject: [PATCH 6/9] subtle changes. --- lib/jenkins_api_client/job.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/jenkins_api_client/job.rb b/lib/jenkins_api_client/job.rb index 0d437f86..a1faba1b 100644 --- a/lib/jenkins_api_client/job.rb +++ b/lib/jenkins_api_client/job.rb @@ -1,4 +1,4 @@ -# + # Copyright (c) 2012-2013 Kannan Manickam # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -689,20 +689,20 @@ def enabled = false } [ - _class: job.getClass().getCanonicalName(), + _class: job.getClass().getCanonicalName().toString(), name: path, url: Hudson.getInstance().getRootUrl().toString() + job.getUrl().toString(), ] } def builder = new groovy.json.JsonBuilder(allInfo) - out.println(builder.toPrettyString()) + out.println(builder.toString()) } job_list_json() EOS - response_json = JSON.parse(groovy_script_output) - response_json = { "jobs" => response_json } + + response_json = { "jobs" => JSON.parse(groovy_script_output) } jobs = [] response_json["jobs"].each do |job| @@ -714,7 +714,7 @@ def builder = new groovy.json.JsonBuilder(allInfo) end end end - jobs + JSON.parse(jobs) end # List all jobs on the Jenkins CI server along with their details From 54dc97e20d6c4c3862f520fed662aa33687fcce9 Mon Sep 17 00:00:00 2001 From: Sahal Ansari Date: Mon, 3 Apr 2017 16:16:22 -0500 Subject: [PATCH 7/9] tailor-ci test. --- lib/jenkins_api_client/job.rb | 5 +++-- spec/unit_tests/job_spec.rb | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/jenkins_api_client/job.rb b/lib/jenkins_api_client/job.rb index a1faba1b..63c68447 100644 --- a/lib/jenkins_api_client/job.rb +++ b/lib/jenkins_api_client/job.rb @@ -703,8 +703,9 @@ def builder = new groovy.json.JsonBuilder(allInfo) EOS response_json = { "jobs" => JSON.parse(groovy_script_output) } + response_json = response_json - jobs = [] + jobs = Array.new response_json["jobs"].each do |job| if job["_class"] !~ /com.cloudbees.hudson.plugins.folder.Folder/ if ignorecase @@ -714,7 +715,7 @@ def builder = new groovy.json.JsonBuilder(allInfo) end end end - JSON.parse(jobs) + jobs.to_a end # List all jobs on the Jenkins CI server along with their details diff --git a/spec/unit_tests/job_spec.rb b/spec/unit_tests/job_spec.rb index acb96bf5..33541116 100644 --- a/spec/unit_tests/job_spec.rb +++ b/spec/unit_tests/job_spec.rb @@ -45,7 +45,7 @@ mock_job_list_response = { "jobs" => [] } # job response w/ 0 jobs - @client.should_receive(:api_get_request).with('').and_return(mock_job_list_response) + @client.should_receive(:api_get_request).with('', "tree=useCrumbs").and_return(mock_job_list_response) @job.should_receive(:create).with(job_name, xml).and_return(nil) @job.create_or_update(job_name, xml) @@ -57,7 +57,7 @@ mock_job_list_response = { "jobs" => [ { "name" => job_name } ] } # job response w/ 1 job - @client.should_receive(:api_get_request).with('').and_return(mock_job_list_response) + @client.should_receive(:api_get_request).with('', "tree=useCrumbs").and_return(mock_job_list_response) @job.should_receive(:update).with(job_name, xml).and_return(nil) @job.create_or_update(job_name, xml) @@ -275,7 +275,7 @@ describe "#exists?" do it "accepts a job name and returns true if the job exists" do - @client.should_receive(:api_get_request).and_return( + @client.should_receive(:api_post_request).and_return( @sample_json_response) @job.exists?("test_job").should == true end @@ -296,7 +296,7 @@ describe "#list" do it "accepts a filter and returns all jobs matching the filter" do - @client.should_receive(:api_get_request).and_return( + @client.should_receive(:api_post_request).and_return( "jobs" => ["test_job"]) @job.list("filter").class.should == Array end From e399a0c9eb1a09f9166d702bdba6af0259e6629b Mon Sep 17 00:00:00 2001 From: Sunil Chopra Date: Tue, 4 Apr 2017 15:46:53 -0500 Subject: [PATCH 8/9] fixed unit tests mostly fixes to mock usage of client object --- lib/jenkins_api_client/job.rb | 17 ++++----- spec/unit_tests/job_spec.rb | 67 ++++++++++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/lib/jenkins_api_client/job.rb b/lib/jenkins_api_client/job.rb index 63c68447..5a1e791b 100644 --- a/lib/jenkins_api_client/job.rb +++ b/lib/jenkins_api_client/job.rb @@ -702,17 +702,18 @@ def builder = new groovy.json.JsonBuilder(allInfo) job_list_json() EOS - response_json = { "jobs" => JSON.parse(groovy_script_output) } - response_json = response_json + response_json = groovy_script_output jobs = Array.new response_json["jobs"].each do |job| - if job["_class"] !~ /com.cloudbees.hudson.plugins.folder.Folder/ - if ignorecase - jobs << job["name"] if job["name"] =~ /#{filter}/i - else - jobs << job["name"] if job["name"] =~ /#{filter}/ - end + if job.is_a?(Hash) + if job.key? :_class and job[:_class] !~ /com.cloudbees.hudson.plugins.folder.Folder/ + if ignorecase + jobs << job[:name] if job[:name] =~ /#{filter}/i + else + jobs << job[:name] if job[:name] =~ /#{filter}/ + end + end end end jobs.to_a diff --git a/spec/unit_tests/job_spec.rb b/spec/unit_tests/job_spec.rb index 33541116..502cff23 100644 --- a/spec/unit_tests/job_spec.rb +++ b/spec/unit_tests/job_spec.rb @@ -28,6 +28,54 @@ end describe "InstanceMethods" do + script_text = <<'EOS' + def Map findJobs(Object obj, String namespace = null) { + def found = [:] + + // groovy apparently can't #collect on a list and return a map? + obj.items.each { job -> + // a possibly better approach would be to walk the parent chain from // + // each job + def path = job.getName() + if (namespace) { + path = "${namespace}/" + path + } + found[path] = job + // intentionally not using `instanceof` here so we don't blow up if the + // cloudbees-folder plugin is not installed + if (job.getClass().getName() == 'com.cloudbees.hudson.plugins.folder.Folder') { + found << findJobs(job, path) + } + } + + found + } + + void job_list_json() { + def jobs = findJobs(Jenkins.getInstance()) + + def allInfo = jobs.collect { path, job -> + // at least these job classes do not respond to respond to #isDisabled: + // - org.jenkinsci.plugins.workflow.job.WorkflowJob + // - com.cloudbees.hudson.plugins.folder.Folder + def enabled = false + if (job.metaClass.respondsTo(job, 'isDisabled')) { + enabled = !job.isDisabled() + } + + [ + _class: job.getClass().getCanonicalName().toString(), + name: path, + url: Hudson.getInstance().getRootUrl().toString() + job.getUrl().toString(), + ] + } + + def builder = new groovy.json.JsonBuilder(allInfo) + out.println(builder.toString()) + } + + job_list_json() +EOS describe "#create_job" do it "accepts job_name and xml and creates the job" do @@ -45,7 +93,7 @@ mock_job_list_response = { "jobs" => [] } # job response w/ 0 jobs - @client.should_receive(:api_get_request).with('', "tree=useCrumbs").and_return(mock_job_list_response) + @client.should_receive(:api_post_request).with('/scriptText', {'script' => script_text}, true).and_return(FakeResponse.new(200,mock_job_list_response)) @job.should_receive(:create).with(job_name, xml).and_return(nil) @job.create_or_update(job_name, xml) @@ -55,9 +103,9 @@ job_name = 'test_job' xml = 'somename' - mock_job_list_response = { "jobs" => [ { "name" => job_name } ] } # job response w/ 1 job + mock_job_list_response = { "jobs" => [ { "_class": "hudson.model.FreeStyleProject", "name": job_name,"url": "https://jenkins.example.com/job/#{job_name}/"} ] } - @client.should_receive(:api_get_request).with('', "tree=useCrumbs").and_return(mock_job_list_response) + @client.should_receive(:api_post_request).with('/scriptText', {'script' => script_text}, true).and_return(FakeResponse.new(200,mock_job_list_response)) @job.should_receive(:update).with(job_name, xml).and_return(nil) @job.create_or_update(job_name, xml) @@ -275,9 +323,10 @@ describe "#exists?" do it "accepts a job name and returns true if the job exists" do - @client.should_receive(:api_post_request).and_return( - @sample_json_response) - @job.exists?("test_job").should == true + job_name = 'test_job' + mock_job_list_response = { "jobs" => [ { "_class": "hudson.model.FreeStyleProject", "name": job_name,"url": "https://jenkins.example.com/job/#{job_name}/"} ] } + @client.should_receive(:api_post_request).with('/scriptText', {'script' => script_text}, true).and_return(FakeResponse.new(200,mock_job_list_response)) + @job.exists?(job_name).should == true end end @@ -296,8 +345,10 @@ describe "#list" do it "accepts a filter and returns all jobs matching the filter" do - @client.should_receive(:api_post_request).and_return( - "jobs" => ["test_job"]) + job_name = 'test_job' + mock_job_list_response = { "jobs" => [ { "_class": "com.cloudbees.hudson.plugins.folder.Folder", "name": job_name,"url": "https://jenkins.example.com/job/#{job_name}/"} ] } + @client.should_receive(:api_post_request).with('/scriptText', {'script' => script_text}, true).and_return(FakeResponse.new(200,mock_job_list_response)) + @job.list("filter").class.should == Array end end From 9af3e1d9f86e7f937e164e427f5c765ccd2ba15c Mon Sep 17 00:00:00 2001 From: Sunil Chopra Date: Tue, 4 Apr 2017 15:47:29 -0500 Subject: [PATCH 9/9] added new test to ensure that folder names do not trigger false positives --- spec/unit_tests/job_spec.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/spec/unit_tests/job_spec.rb b/spec/unit_tests/job_spec.rb index 502cff23..dde6d86a 100644 --- a/spec/unit_tests/job_spec.rb +++ b/spec/unit_tests/job_spec.rb @@ -328,6 +328,14 @@ def builder = new groovy.json.JsonBuilder(allInfo) @client.should_receive(:api_post_request).with('/scriptText', {'script' => script_text}, true).and_return(FakeResponse.new(200,mock_job_list_response)) @job.exists?(job_name).should == true end + + it "does not match folder names" do + job_name = 'test_job' + mock_job_list_response = { "jobs" => [ { "_class": "com.cloudbees.hudson.plugins.folder.Folder", "name": job_name,"url": "https://jenkins.example.com/job/#{job_name}/"} ] } + @client.should_receive(:api_post_request).with('/scriptText', {'script' => script_text}, true).and_return(FakeResponse.new(200,mock_job_list_response)) + + @job.exists?(job_name).should == false + end end describe "#list_by_status" do