jenkins_api_client 0.14.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.travis.yml +5 -23
- data/CHANGELOG.md +104 -35
- data/CONTRIBUTORS.md +1 -11
- data/Gemfile +4 -2
- data/README.md +107 -7
- data/Rakefile +1 -0
- data/Vagrantfile +6 -8
- data/jenkins_api_client.gemspec +30 -12
- data/lib/jenkins_api_client/cli/helper.rb +1 -0
- data/lib/jenkins_api_client/client.rb +162 -69
- data/lib/jenkins_api_client/exceptions.rb +26 -17
- data/lib/jenkins_api_client/job.rb +321 -75
- data/lib/jenkins_api_client/node.rb +22 -10
- data/lib/jenkins_api_client/plugin_manager.rb +460 -0
- data/lib/jenkins_api_client/urihelper.rb +17 -0
- data/lib/jenkins_api_client/user.rb +4 -2
- data/lib/jenkins_api_client/version.rb +3 -3
- data/lib/jenkins_api_client/view.rb +10 -7
- data/lib/jenkins_api_client.rb +1 -0
- data/scripts/login_with_pry.rb +54 -0
- data/spec/func_tests/client_spec.rb +3 -3
- data/spec/func_tests/job_spec.rb +90 -7
- data/spec/func_tests/{node_spec.rb → node_spec.rb.pending} +9 -9
- data/spec/func_tests/plugin_spec.rb +148 -0
- data/spec/unit_tests/client_spec.rb +108 -27
- data/spec/unit_tests/fake_http_response.rb +9 -0
- data/spec/unit_tests/fixtures/files/available_plugins.json +1 -0
- data/spec/unit_tests/fixtures/files/installed_plugins.json +1 -0
- data/spec/unit_tests/fixtures/files/updatable_plugins.json +1 -0
- data/spec/unit_tests/job_spec.rb +109 -6
- data/spec/unit_tests/node_spec.rb +18 -6
- data/spec/unit_tests/plugin_spec.rb +165 -0
- data/spec/unit_tests/spec_helper.rb +11 -1
- data/spec/unit_tests/system_spec.rb +2 -1
- data/spec/unit_tests/user_spec.rb +1 -1
- data/travis/hudson.model.UpdateCenter.xml +7 -0
- data/travis/setup.sh +2 -1
- metadata +76 -64
@@ -20,12 +20,18 @@
|
|
20
20
|
# THE SOFTWARE.
|
21
21
|
#
|
22
22
|
|
23
|
+
require 'jenkins_api_client/urihelper'
|
24
|
+
|
23
25
|
module JenkinsApi
|
24
26
|
class Client
|
25
27
|
# This class communicates with the Jenkins "/job" API to obtain details
|
26
28
|
# about jobs, creating, deleting, building, and various other operations.
|
27
29
|
#
|
28
30
|
class Job
|
31
|
+
include JenkinsApi::UriHelper
|
32
|
+
|
33
|
+
# Version that jenkins started to include queued build info in build response
|
34
|
+
JENKINS_QUEUE_ID_SUPPORT_VERSION = '1.519'
|
29
35
|
|
30
36
|
# Initialize the Job object and store the reference to Client object
|
31
37
|
#
|
@@ -74,7 +80,7 @@ module JenkinsApi
|
|
74
80
|
#
|
75
81
|
def create(job_name, xml)
|
76
82
|
@logger.info "Creating job '#{job_name}'"
|
77
|
-
@client.post_config("/createItem?name=#{job_name}", xml)
|
83
|
+
@client.post_config("/createItem?name=#{form_encode job_name}", xml)
|
78
84
|
end
|
79
85
|
|
80
86
|
# Update a job with the name specified and the xml given
|
@@ -197,7 +203,7 @@ module JenkinsApi
|
|
197
203
|
# Update a job with params given as a hash instead of the xml. For the
|
198
204
|
# parameter description see #create_or_update_freestyle
|
199
205
|
#
|
200
|
-
# @param params [Hash]
|
206
|
+
# @param params [Hash] parameters to update a freestyle project
|
201
207
|
#
|
202
208
|
# @see #create_or_update_freestyle
|
203
209
|
# @see #update
|
@@ -334,7 +340,7 @@ module JenkinsApi
|
|
334
340
|
# @option params [String] :name Name of the job
|
335
341
|
# @option params [String] :notification_email Email address to send
|
336
342
|
# @option params [Boolean] :notification_email_for_every_unstable
|
337
|
-
#
|
343
|
+
# Send email notification email for every unstable build
|
338
344
|
#
|
339
345
|
def add_email_notification(params)
|
340
346
|
raise "No job name specified" unless params[:name]
|
@@ -400,16 +406,18 @@ module JenkinsApi
|
|
400
406
|
#
|
401
407
|
def rename(old_job, new_job)
|
402
408
|
@logger.info "Renaming job '#{old_job}' to '#{new_job}'"
|
403
|
-
@client.api_post_request("/job/#{old_job}/doRename?newName=#{new_job}")
|
409
|
+
@client.api_post_request("/job/#{path_encode old_job}/doRename?newName=#{form_encode new_job}")
|
404
410
|
end
|
405
411
|
|
406
412
|
# Delete a job given the name
|
407
413
|
#
|
408
|
-
# @param [String]
|
414
|
+
# @param job_name [String] the name of the job to delete
|
415
|
+
#
|
416
|
+
# @return [String] the response from the HTTP POST request
|
409
417
|
#
|
410
418
|
def delete(job_name)
|
411
419
|
@logger.info "Deleting job '#{job_name}'"
|
412
|
-
@client.api_post_request("/job/#{job_name}/doDelete")
|
420
|
+
@client.api_post_request("/job/#{path_encode job_name}/doDelete")
|
413
421
|
end
|
414
422
|
|
415
423
|
# Deletes all jobs from Jenkins
|
@@ -424,11 +432,13 @@ module JenkinsApi
|
|
424
432
|
|
425
433
|
# Wipe out the workspace for a job given the name
|
426
434
|
#
|
427
|
-
# @param [String]
|
435
|
+
# @param job_name [String] the name of the job to wipe out the workspace
|
436
|
+
#
|
437
|
+
# @return [String] response from the HTTP POST request
|
428
438
|
#
|
429
439
|
def wipe_out_workspace(job_name)
|
430
440
|
@logger.info "Wiping out the workspace of job '#{job_name}'"
|
431
|
-
@client.api_post_request("/job/#{job_name}/doWipeOutWorkspace")
|
441
|
+
@client.api_post_request("/job/#{path_encode job_name}/doWipeOutWorkspace")
|
432
442
|
end
|
433
443
|
|
434
444
|
# Stops a running build of a job
|
@@ -436,8 +446,8 @@ module JenkinsApi
|
|
436
446
|
# is specified. The build will be stopped only if it was
|
437
447
|
# in 'running' state.
|
438
448
|
#
|
439
|
-
# @param [String]
|
440
|
-
# @param [Number]
|
449
|
+
# @param job_name [String] the name of the job to stop the build
|
450
|
+
# @param build_number [Number] the build number to stop
|
441
451
|
#
|
442
452
|
def stop_build(job_name, build_number = 0)
|
443
453
|
build_number = get_current_build_number(job_name) if build_number == 0
|
@@ -445,17 +455,21 @@ module JenkinsApi
|
|
445
455
|
@logger.info "Stopping job '#{job_name}' Build ##{build_number}"
|
446
456
|
# Check and see if the build is running
|
447
457
|
is_building = @client.api_get_request(
|
448
|
-
"/job/#{job_name}/#{build_number}"
|
458
|
+
"/job/#{path_encode job_name}/#{build_number}"
|
449
459
|
)["building"]
|
450
460
|
if is_building
|
451
|
-
@client.api_post_request("/job/#{job_name}/#{build_number}/stop")
|
461
|
+
@client.api_post_request("/job/#{path_encode job_name}/#{build_number}/stop")
|
452
462
|
end
|
453
463
|
end
|
464
|
+
alias_method :stop, :stop_build
|
465
|
+
alias_method :abort, :stop_build
|
454
466
|
|
455
467
|
# Re-create the same job
|
456
468
|
# This is a hack to clear any existing builds
|
457
469
|
#
|
458
|
-
# @param [String]
|
470
|
+
# @param job_name [String] the name of the job to recreate
|
471
|
+
#
|
472
|
+
# @return [String] the response from the HTTP POST request
|
459
473
|
#
|
460
474
|
def recreate(job_name)
|
461
475
|
@logger.info "Recreating job '#{job_name}'"
|
@@ -466,14 +480,16 @@ module JenkinsApi
|
|
466
480
|
|
467
481
|
# Copy a job
|
468
482
|
#
|
469
|
-
# @param [String]
|
470
|
-
# @param [String]
|
483
|
+
# @param from_job_name [String] the name of the job to copy from
|
484
|
+
# @param to_job_name [String] the name of the job to copy to
|
485
|
+
#
|
486
|
+
# @return [String] the response from the HTTP POST request
|
471
487
|
#
|
472
488
|
def copy(from_job_name, to_job_name=nil)
|
473
489
|
to_job_name = "copy_of_#{from_job_name}" if to_job_name.nil?
|
474
490
|
@logger.info "Copying job '#{from_job_name}' to '#{to_job_name}'"
|
475
491
|
@client.api_post_request(
|
476
|
-
"/createItem?name=#{to_job_name}&mode=copy&from=#{from_job_name}"
|
492
|
+
"/createItem?name=#{path_encode to_job_name}&mode=copy&from=#{path_encode from_job_name}"
|
477
493
|
)
|
478
494
|
end
|
479
495
|
|
@@ -481,7 +497,7 @@ module JenkinsApi
|
|
481
497
|
#
|
482
498
|
# @param [String] job_name Name of the Jenkins job
|
483
499
|
# @param [Number] build_num Specific build number to obtain the
|
484
|
-
#
|
500
|
+
# console output from. Default is the recent build
|
485
501
|
# @param [Number] start start offset to get only a portion of the text
|
486
502
|
# @param [String] mode Mode of text output. 'text' or 'html'
|
487
503
|
#
|
@@ -505,7 +521,7 @@ module JenkinsApi
|
|
505
521
|
else
|
506
522
|
raise "Mode should either be 'text' or 'html'. You gave: #{mode}"
|
507
523
|
end
|
508
|
-
get_msg = "/job/#{job_name}/#{build_num}/logText/progressive#{mode}?"
|
524
|
+
get_msg = "/job/#{path_encode job_name}/#{build_num}/logText/progressive#{mode}?"
|
509
525
|
get_msg << "start=#{start}"
|
510
526
|
raw_response = true
|
511
527
|
api_response = @client.api_get_request(get_msg, nil, nil, raw_response)
|
@@ -520,6 +536,8 @@ module JenkinsApi
|
|
520
536
|
|
521
537
|
# List all jobs on the Jenkins CI server
|
522
538
|
#
|
539
|
+
# @return [Array<String>] the names of all jobs in jenkins
|
540
|
+
#
|
523
541
|
def list_all
|
524
542
|
response_json = @client.api_get_request("", "tree=jobs[name]")["jobs"]
|
525
543
|
response_json.map { |job| job["name"] }.sort
|
@@ -527,7 +545,9 @@ module JenkinsApi
|
|
527
545
|
|
528
546
|
# Checks if the given job exists in Jenkins
|
529
547
|
#
|
530
|
-
# @param [String]
|
548
|
+
# @param job_name [String] the name of the job to check
|
549
|
+
#
|
550
|
+
# @return [Boolean] whether the job exists in jenkins or not
|
531
551
|
#
|
532
552
|
def exists?(job_name)
|
533
553
|
list(job_name).include?(job_name)
|
@@ -536,8 +556,12 @@ module JenkinsApi
|
|
536
556
|
# List all Jobs matching the given status
|
537
557
|
# You can optionally pass in jobs list to filter the status from
|
538
558
|
#
|
539
|
-
# @param [String] status
|
540
|
-
# @param [Array]
|
559
|
+
# @param status [String] the job status to filter
|
560
|
+
# @param jobs [Array<String>] if specified this array will be used for
|
561
|
+
# filtering by the status otherwise the filtering will be done using
|
562
|
+
# all jobs available in jenkins
|
563
|
+
#
|
564
|
+
# @return [Array<String>] filtered jobs
|
541
565
|
#
|
542
566
|
def list_by_status(status, jobs = [])
|
543
567
|
jobs = list_all if jobs.empty?
|
@@ -555,8 +579,10 @@ module JenkinsApi
|
|
555
579
|
|
556
580
|
# List all jobs that match the given regex
|
557
581
|
#
|
558
|
-
# @param [String]
|
559
|
-
# @param [Boolean]
|
582
|
+
# @param filter [String] a regular expression or a string to filter jobs
|
583
|
+
# @param ignorecase [Boolean] whether to ignore case or not
|
584
|
+
#
|
585
|
+
# @return [Array<String>] jobs matching the given pattern
|
560
586
|
#
|
561
587
|
def list(filter, ignorecase = true)
|
562
588
|
@logger.info "Obtaining jobs matching filter '#{filter}'"
|
@@ -574,6 +600,8 @@ module JenkinsApi
|
|
574
600
|
|
575
601
|
# List all jobs on the Jenkins CI server along with their details
|
576
602
|
#
|
603
|
+
# @return [Array<Hash>] the details of all jobs in jenkins
|
604
|
+
#
|
577
605
|
def list_all_with_details
|
578
606
|
@logger.info "Obtaining the details of all jobs"
|
579
607
|
response_json = @client.api_get_request("")
|
@@ -582,30 +610,34 @@ module JenkinsApi
|
|
582
610
|
|
583
611
|
# List details of a specific job
|
584
612
|
#
|
585
|
-
# @param [String]
|
613
|
+
# @param job_name [String] the name of the job to obtain the details from
|
614
|
+
#
|
615
|
+
# @return [Hash] the details of the specified job
|
586
616
|
#
|
587
617
|
def list_details(job_name)
|
588
618
|
@logger.info "Obtaining the details of '#{job_name}'"
|
589
|
-
@client.api_get_request("/job/#{job_name}")
|
619
|
+
@client.api_get_request("/job/#{path_encode job_name}")
|
590
620
|
end
|
591
621
|
|
592
622
|
# List upstream projects of a specific job
|
593
623
|
#
|
594
|
-
# @param [String]
|
624
|
+
# @param job_name [String] the name of the job to obtain upstream
|
625
|
+
# projects for
|
595
626
|
#
|
596
627
|
def get_upstream_projects(job_name)
|
597
628
|
@logger.info "Obtaining the upstream projects of '#{job_name}'"
|
598
|
-
response_json = @client.api_get_request("/job/#{job_name}")
|
629
|
+
response_json = @client.api_get_request("/job/#{path_encode job_name}")
|
599
630
|
response_json["upstreamProjects"]
|
600
631
|
end
|
601
632
|
|
602
633
|
# List downstream projects of a specific job
|
603
634
|
#
|
604
|
-
# @param [String]
|
635
|
+
# @param job_name [String] the name of the job to obtain downstream
|
636
|
+
# projects for
|
605
637
|
#
|
606
638
|
def get_downstream_projects(job_name)
|
607
639
|
@logger.info "Obtaining the down stream projects of '#{job_name}'"
|
608
|
-
response_json = @client.api_get_request("/job/#{job_name}")
|
640
|
+
response_json = @client.api_get_request("/job/#{path_encode job_name}")
|
609
641
|
response_json["downstreamProjects"]
|
610
642
|
end
|
611
643
|
|
@@ -615,7 +647,7 @@ module JenkinsApi
|
|
615
647
|
#
|
616
648
|
def get_builds(job_name)
|
617
649
|
@logger.info "Obtaining the build details of '#{job_name}'"
|
618
|
-
response_json = @client.api_get_request("/job/#{job_name}")
|
650
|
+
response_json = @client.api_get_request("/job/#{path_encode job_name}")
|
619
651
|
response_json["builds"]
|
620
652
|
end
|
621
653
|
|
@@ -657,9 +689,10 @@ module JenkinsApi
|
|
657
689
|
#
|
658
690
|
def get_current_build_status(job_name)
|
659
691
|
@logger.info "Obtaining the current build status of '#{job_name}'"
|
660
|
-
response_json = @client.api_get_request("/job/#{job_name}")
|
692
|
+
response_json = @client.api_get_request("/job/#{path_encode job_name}")
|
661
693
|
color_to_status(response_json["color"])
|
662
694
|
end
|
695
|
+
alias_method :status, :get_current_build_status
|
663
696
|
|
664
697
|
# Obtain the current build number of the given job
|
665
698
|
# This function returns nil if there were no builds for the given job.
|
@@ -670,60 +703,248 @@ module JenkinsApi
|
|
670
703
|
#
|
671
704
|
def get_current_build_number(job_name)
|
672
705
|
@logger.info "Obtaining the current build number of '#{job_name}'"
|
673
|
-
@client.api_get_request("/job/#{job_name}")['nextBuildNumber'].to_i - 1
|
706
|
+
@client.api_get_request("/job/#{path_encode job_name}")['nextBuildNumber'].to_i - 1
|
674
707
|
end
|
675
|
-
|
676
|
-
|
677
|
-
#
|
678
|
-
#
|
708
|
+
alias_method :build_number, :get_current_build_number
|
709
|
+
|
710
|
+
# Build a Jenkins job, optionally waiting for build to start and
|
711
|
+
# returning the build number.
|
712
|
+
# Adds support for new/old Jenkins servers where build_queue id may
|
713
|
+
# not be available. Also adds support for periodic callbacks, and
|
714
|
+
# optional cancellation of queued_job if not started within allowable
|
715
|
+
# time window (if build_queue option available)
|
716
|
+
#
|
717
|
+
# Notes:
|
718
|
+
# 'opts' may be a 'true' or 'false' value to maintain
|
719
|
+
# compatibility with old method signature, where true indicates
|
720
|
+
# 'return_build_number'. In this case, true is translated to:
|
721
|
+
# { 'build_start_timeout' => @client_timeout }
|
722
|
+
# which simulates earlier behavior.
|
723
|
+
#
|
724
|
+
# progress_proc
|
725
|
+
# Optional proc that is called periodically while waiting for
|
726
|
+
# build to start.
|
727
|
+
# Initial call (with poll_count == 0) indicates build has been
|
728
|
+
# requested, and that polling is starting.
|
729
|
+
# Final call will indicate one of build_started or cancelled.
|
730
|
+
# params:
|
731
|
+
# max_wait [Integer] Same as opts['build_start_timeout']
|
732
|
+
# current_wait [Integer]
|
733
|
+
# poll_count [Integer] How many times has queue been polled
|
734
|
+
#
|
735
|
+
# completion_proc
|
736
|
+
# Optional proc that is called <just before> the 'build' method
|
737
|
+
# exits.
|
738
|
+
# params:
|
739
|
+
# build_number [Integer] Present if build started or nil
|
740
|
+
# build_cancelled [Boolean] True if build timed out and was
|
741
|
+
# successfully removed from build-queue
|
679
742
|
#
|
680
743
|
# @param [String] job_name the name of the job
|
681
|
-
# @param [Hash]
|
682
|
-
# @param [
|
683
|
-
#
|
684
|
-
#
|
685
|
-
#
|
686
|
-
#
|
687
|
-
#
|
688
|
-
#
|
689
|
-
#
|
690
|
-
#
|
691
|
-
|
744
|
+
# @param [Hash] params the parameters for parameterized build
|
745
|
+
# @param [Hash] opts options for this method
|
746
|
+
# * +build_start_timeout+ [Integer] How long to wait for queued
|
747
|
+
# build to start before giving up. Default: 0/nil
|
748
|
+
# * +cancel_on_build_start_timeout+ [Boolean] Should an attempt be
|
749
|
+
# made to cancel the queued build if it hasn't started within
|
750
|
+
# 'build_start_timeout' seconds? This only works on newer versions
|
751
|
+
# of Jenkins where JobQueue is exposed in build post response.
|
752
|
+
# Default: false
|
753
|
+
# * +poll_interval+ [Integer] How often should we check with CI
|
754
|
+
# Server while waiting for start. Default: 2 (seconds)
|
755
|
+
# * +progress_proc+ [Proc] A proc that will receive progress notitications. Default: nil
|
756
|
+
# * +completion_proc+ [Proc] A proc that is called <just before>
|
757
|
+
# this method (build) exits. Default: nil
|
758
|
+
#
|
759
|
+
# @return [Integer] build number, or nil if not started (IF TIMEOUT SPECIFIED)
|
760
|
+
# @return [Integer] HTTP response code (per prev. behavior) (NO TIMEOUT SPECIFIED)
|
761
|
+
#
|
762
|
+
def build(job_name, params={}, opts = {})
|
763
|
+
if opts.nil? || opts.class.is_a?(FalseClass)
|
764
|
+
opts = {}
|
765
|
+
elsif opts.class.is_a?(TrueClass)
|
766
|
+
opts = { 'build_start_timeout' => @client_timeout }
|
767
|
+
end
|
768
|
+
|
769
|
+
opts['job_name'] = job_name
|
770
|
+
|
692
771
|
msg = "Building job '#{job_name}'"
|
693
772
|
msg << " with parameters: #{params.inspect}" unless params.empty?
|
694
773
|
@logger.info msg
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
774
|
+
|
775
|
+
# Best-guess build-id
|
776
|
+
# This is only used if we go the old-way below... but we can use this number to detect if multiple
|
777
|
+
# builds were queued
|
778
|
+
current_build_id = get_current_build_number(job_name)
|
779
|
+
expected_build_id = current_build_id > 0 ? current_build_id + 1 : 1
|
780
|
+
|
781
|
+
if (params.nil? or params.empty?)
|
782
|
+
response = @client.api_post_request("/job/#{path_encode job_name}/build",
|
783
|
+
{},
|
784
|
+
true)
|
785
|
+
else
|
786
|
+
response = @client.api_post_request("/job/#{path_encode job_name}/buildWithParameters",
|
787
|
+
params,
|
788
|
+
true)
|
789
|
+
end
|
790
|
+
|
791
|
+
if (opts['build_start_timeout'] || 0) > 0
|
792
|
+
if @client.compare_versions(@client.get_jenkins_version, JENKINS_QUEUE_ID_SUPPORT_VERSION) >= 0
|
793
|
+
return get_build_id_from_queue(response, expected_build_id, opts)
|
794
|
+
else
|
795
|
+
return get_build_id_the_old_way(expected_build_id, opts)
|
796
|
+
end
|
797
|
+
else
|
798
|
+
return response.code
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
def get_build_id_from_queue(response, expected_build_id, opts)
|
803
|
+
# If we get this far the API hasn't detected an error response (it would raise Exception)
|
804
|
+
# So no need to check response code
|
805
|
+
# Obtain the queue ID from the location
|
703
806
|
# header and wait till the build is moved to one of the executors and a
|
704
807
|
# build number is assigned
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
808
|
+
build_start_timeout = opts['build_start_timeout']
|
809
|
+
poll_interval = opts['poll_interval'] || 2
|
810
|
+
poll_interval = 1 if poll_interval < 1
|
811
|
+
progress_proc = opts['progress_proc']
|
812
|
+
completion_proc = opts['completion_proc']
|
813
|
+
job_name = opts['job_name']
|
814
|
+
|
815
|
+
if response["location"]
|
816
|
+
task_id_match = response["location"].match(/\/item\/(\d*)\//)
|
817
|
+
task_id = task_id_match.nil? ? nil : task_id_match[1]
|
818
|
+
unless task_id.nil?
|
819
|
+
@logger.info "Job queued for #{job_name}, will wait up to #{build_start_timeout} seconds for build to start..."
|
820
|
+
|
821
|
+
# Let progress proc know we've queued the build
|
822
|
+
progress_proc.call(build_start_timeout, 0, 0) if progress_proc
|
823
|
+
|
824
|
+
# Wait for the build to start
|
825
|
+
begin
|
826
|
+
start = Time.now.to_i
|
827
|
+
Timeout::timeout(build_start_timeout) do
|
828
|
+
started = false
|
829
|
+
attempts = 0
|
830
|
+
|
831
|
+
while !started
|
832
|
+
# Don't really care about the response... if we get thru here, then it must have worked.
|
833
|
+
# Jenkins will return 404's until the job starts
|
834
|
+
queue_item = @client.queue.get_item_by_id(task_id)
|
835
|
+
|
836
|
+
if queue_item['executable'].nil?
|
837
|
+
# Job not started yet
|
838
|
+
attempts += 1
|
839
|
+
|
840
|
+
progress_proc.call(build_start_timeout, (Time.now.to_i - start), attempts) if progress_proc
|
841
|
+
# Every 5 attempts (~10 seconds)
|
842
|
+
@logger.info "Still waiting..." if attempts % 5 == 0
|
843
|
+
|
844
|
+
sleep poll_interval
|
845
|
+
else
|
846
|
+
build_number = queue_item['executable']['number']
|
847
|
+
completion_proc.call(build_number, false) if completion_proc
|
848
|
+
|
849
|
+
return build_number
|
850
|
+
end
|
714
851
|
end
|
715
852
|
end
|
716
|
-
|
717
|
-
|
718
|
-
|
853
|
+
rescue Timeout::Error
|
854
|
+
# Well, we waited - and the job never started building
|
855
|
+
# Attempt to kill off queued job (if flag set)
|
856
|
+
if opts['cancel_on_build_start_timeout']
|
857
|
+
@logger.info "Job for '#{job_name}' did not start in a timely manner, attempting to cancel pending build..."
|
858
|
+
|
859
|
+
begin
|
860
|
+
@client.api_post_request("/queue/cancelItem?id=#{task_id}")
|
861
|
+
@logger.info "Job cancelled"
|
862
|
+
completion_proc.call(nil, true) if completion_proc
|
863
|
+
rescue JenkinsApi::Exceptions::ApiException => e
|
864
|
+
completion_proc.call(nil, false) if completion_proc
|
865
|
+
@logger.warn "Error while attempting to cancel pending job for '#{job_name}'. #{e.class} #{e}"
|
866
|
+
raise
|
867
|
+
end
|
868
|
+
else
|
869
|
+
@logger.info "Jenkins build for '#{job_name}' failed to start in a timely manner"
|
870
|
+
completion_proc.call(nil, false) if completion_proc
|
871
|
+
end
|
872
|
+
|
873
|
+
# Old version used to throw timeout error, so we should let that go thru now
|
874
|
+
raise
|
875
|
+
rescue JenkinsApi::Exceptions::ApiException => e
|
876
|
+
# Jenkins Api threw an error at us
|
877
|
+
completion_proc.call(nil, false) if completion_proc
|
878
|
+
@logger.warn "Problem while waiting for '#{job_name}' build to start. #{e.class} #{e}"
|
879
|
+
raise
|
719
880
|
end
|
720
881
|
else
|
721
|
-
|
882
|
+
@logger.warn "Jenkins did not return a queue_id for '#{job_name}' build (location: #{response['location']})"
|
883
|
+
return get_build_id_the_old_way(expected_build_id, opts)
|
722
884
|
end
|
723
885
|
else
|
724
|
-
|
886
|
+
@logger.warn "Jenkins did not return a location header for '#{job_name}' build"
|
887
|
+
return get_build_id_the_old_way(expected_build_id, opts)
|
888
|
+
end
|
889
|
+
end
|
890
|
+
private :get_build_id_from_queue
|
891
|
+
|
892
|
+
def get_build_id_the_old_way(expected_build_id, opts)
|
893
|
+
# Try to wait until the build starts so we can mimic queue
|
894
|
+
# Wait for the build to start
|
895
|
+
build_start_timeout = opts['build_start_timeout']
|
896
|
+
poll_interval = opts['poll_interval'] || 2
|
897
|
+
poll_interval = 1 if poll_interval < 1
|
898
|
+
progress_proc = opts['progress_proc']
|
899
|
+
completion_proc = opts['completion_proc']
|
900
|
+
job_name = opts['job_name']
|
901
|
+
|
902
|
+
@logger.info "Build requested for '#{job_name}', will wait up to #{build_start_timeout} seconds for build to start..."
|
903
|
+
|
904
|
+
# Let progress proc know we've queued the build
|
905
|
+
progress_proc.call(build_start_timeout, 0, 0) if progress_proc
|
906
|
+
|
907
|
+
begin
|
908
|
+
start = Time.now.to_i
|
909
|
+
Timeout::timeout(build_start_timeout) do
|
910
|
+
attempts = 0
|
911
|
+
|
912
|
+
while true
|
913
|
+
attempts += 1
|
914
|
+
|
915
|
+
# Don't really care about the response... if we get thru here, then it must have worked.
|
916
|
+
# Jenkins will return 404's until the job starts
|
917
|
+
begin
|
918
|
+
get_build_details(job_name, expected_build_id)
|
919
|
+
completion_proc.call(expected_build_id, false) if completion_proc
|
920
|
+
|
921
|
+
return expected_build_id
|
922
|
+
rescue JenkinsApi::Exceptions::NotFound => e
|
923
|
+
progress_proc.call(build_start_timeout, (Time.now.to_i - start), attempts) if progress_proc
|
924
|
+
|
925
|
+
# Every 5 attempts (~10 seconds)
|
926
|
+
@logger.info "Still waiting..." if attempts % 5 == 0
|
927
|
+
|
928
|
+
sleep poll_interval
|
929
|
+
end
|
930
|
+
end
|
931
|
+
end
|
932
|
+
rescue Timeout::Error
|
933
|
+
# Well, we waited - and the job never started building
|
934
|
+
# Now we need to raise an exception so that the build can be officially failed
|
935
|
+
completion_proc.call(nil, false) if completion_proc
|
936
|
+
@logger.info "Jenkins '#{job_name}' build failed to start in a timely manner"
|
937
|
+
|
938
|
+
# Old version used to propagate timeout error
|
939
|
+
raise
|
940
|
+
rescue JenkinsApi::Exceptions::ApiException => e
|
941
|
+
completion_proc.call(nil, false) if completion_proc
|
942
|
+
# Jenkins Api threw an error at us
|
943
|
+
@logger.warn "Problem while waiting for '#{job_name}' build ##{expected_build_id} to start. #{e.class} #{e}"
|
944
|
+
raise
|
725
945
|
end
|
726
946
|
end
|
947
|
+
private :get_build_id_the_old_way
|
727
948
|
|
728
949
|
# Programatically schedule SCM polling for the specified job
|
729
950
|
#
|
@@ -742,7 +963,7 @@ module JenkinsApi
|
|
742
963
|
#
|
743
964
|
def enable(job_name)
|
744
965
|
@logger.info "Enabling job '#{job_name}'"
|
745
|
-
@client.api_post_request("/job/#{job_name}/enable")
|
966
|
+
@client.api_post_request("/job/#{path_encode job_name}/enable")
|
746
967
|
end
|
747
968
|
|
748
969
|
# Disable a job given the name of the job
|
@@ -751,7 +972,7 @@ module JenkinsApi
|
|
751
972
|
#
|
752
973
|
def disable(job_name)
|
753
974
|
@logger.info "Disabling job '#{job_name}'"
|
754
|
-
@client.api_post_request("/job/#{job_name}/disable")
|
975
|
+
@client.api_post_request("/job/#{path_encode job_name}/disable")
|
755
976
|
end
|
756
977
|
|
757
978
|
# Obtain the configuration stored in config.xml of a specific job
|
@@ -762,7 +983,7 @@ module JenkinsApi
|
|
762
983
|
#
|
763
984
|
def get_config(job_name)
|
764
985
|
@logger.info "Obtaining the config.xml of '#{job_name}'"
|
765
|
-
@client.get_config("/job/#{job_name}")
|
986
|
+
@client.get_config("/job/#{path_encode job_name}")
|
766
987
|
end
|
767
988
|
|
768
989
|
# Post the configuration of a job given the job name and the config.xml
|
@@ -774,7 +995,7 @@ module JenkinsApi
|
|
774
995
|
#
|
775
996
|
def post_config(job_name, xml)
|
776
997
|
@logger.info "Posting the config.xml of '#{job_name}'"
|
777
|
-
@client.post_config("/job/#{job_name}/config.xml", xml)
|
998
|
+
@client.post_config("/job/#{path_encode job_name}/config.xml", xml)
|
778
999
|
end
|
779
1000
|
|
780
1001
|
# Obtain the test results for a specific build of a job
|
@@ -786,7 +1007,7 @@ module JenkinsApi
|
|
786
1007
|
build_num = get_current_build_number(job_name) if build_num == 0
|
787
1008
|
@logger.info "Obtaining the test results of '#{job_name}'" +
|
788
1009
|
" Build ##{build_num}"
|
789
|
-
@client.api_get_request("/job/#{job_name}/#{build_num}/testReport")
|
1010
|
+
@client.api_get_request("/job/#{path_encode job_name}/#{build_num}/testReport")
|
790
1011
|
rescue Exceptions::NotFound
|
791
1012
|
# Not found is acceptable, as not all builds will have test results
|
792
1013
|
# and this is what jenkins throws at us in that case
|
@@ -803,7 +1024,7 @@ module JenkinsApi
|
|
803
1024
|
@logger.info "Obtaining the build details of '#{job_name}'" +
|
804
1025
|
" Build ##{build_num}"
|
805
1026
|
|
806
|
-
@client.api_get_request("/job/#{job_name}/#{build_num}/")
|
1027
|
+
@client.api_get_request("/job/#{path_encode job_name}/#{build_num}/")
|
807
1028
|
end
|
808
1029
|
|
809
1030
|
# Change the description of a specific job
|
@@ -1179,6 +1400,31 @@ module JenkinsApi
|
|
1179
1400
|
filtered_job_names[0..parallel-1]
|
1180
1401
|
end
|
1181
1402
|
|
1403
|
+
# Get a list of promoted builds for given job
|
1404
|
+
#
|
1405
|
+
# @param [String] job_name
|
1406
|
+
# @return [Hash] Hash map of promitions and the promoted builds. Promotions that didn't took place yet
|
1407
|
+
# return nil
|
1408
|
+
def get_promotions(job_name)
|
1409
|
+
result = {}
|
1410
|
+
|
1411
|
+
@logger.info "Obtaining the promotions of '#{job_name}'"
|
1412
|
+
response_json = @client.api_get_request("/job/#{job_name}/promotion")
|
1413
|
+
|
1414
|
+
response_json["processes"].each do |promotion|
|
1415
|
+
@logger.info "Getting promotion details of '#{promotion['name']}'"
|
1416
|
+
|
1417
|
+
if promotion['color'] == 'notbuilt'
|
1418
|
+
result[promotion['name']] = nil
|
1419
|
+
else
|
1420
|
+
promo_json = @client.api_get_request("/job/#{job_name}/promotion/latest/#{promotion['name']}")
|
1421
|
+
result[promotion['name']] = promo_json['target']['number']
|
1422
|
+
end
|
1423
|
+
end
|
1424
|
+
|
1425
|
+
result
|
1426
|
+
end
|
1427
|
+
|
1182
1428
|
private
|
1183
1429
|
|
1184
1430
|
# Obtains the threshold params used by jenkins in the XML file
|