jujube 0.5.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 +18 -0
- data/.travis.yml +9 -0
- data/.yardopts +4 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +180 -0
- data/Rakefile +15 -0
- data/acceptance/fixtures/endToEnd/endToEnd.job +43 -0
- data/acceptance/fixtures/endToEnd/expected.yml +80 -0
- data/acceptance/fixtures/multipleFiles/job1.job +1 -0
- data/acceptance/fixtures/multipleFiles/job2.job +1 -0
- data/acceptance/fixtures/multipleJobs/test.job +2 -0
- data/acceptance/fixtures/nestedDirectories/job1.job +1 -0
- data/acceptance/fixtures/nestedDirectories/nested/job2.job +1 -0
- data/acceptance/fixtures/singleJob/test.job +3 -0
- data/acceptance/jujube/acceptance_test.rb +47 -0
- data/acceptance/jujube/end_to_end_test.rb +13 -0
- data/acceptance/jujube/usage_test.rb +90 -0
- data/bin/jujube +5 -0
- data/jujube.gemspec +29 -0
- data/lib/jujube.rb +6 -0
- data/lib/jujube/components.rb +23 -0
- data/lib/jujube/components/builders.rb +19 -0
- data/lib/jujube/components/helpers.rb +40 -0
- data/lib/jujube/components/macros.rb +31 -0
- data/lib/jujube/components/notifications.rb +11 -0
- data/lib/jujube/components/parameters.rb +50 -0
- data/lib/jujube/components/publishers.rb +97 -0
- data/lib/jujube/components/scm.rb +19 -0
- data/lib/jujube/components/triggers.rb +105 -0
- data/lib/jujube/components/wrappers.rb +28 -0
- data/lib/jujube/driver.rb +78 -0
- data/lib/jujube/dsl.rb +43 -0
- data/lib/jujube/job.rb +226 -0
- data/lib/jujube/job_file_generator.rb +17 -0
- data/lib/jujube/job_loader.rb +51 -0
- data/lib/jujube/macros.rb +35 -0
- data/lib/jujube/utils.rb +27 -0
- data/lib/jujube/version.rb +5 -0
- data/test/components/builders_test.rb +10 -0
- data/test/components/parameters_test.rb +15 -0
- data/test/components/publishers_test.rb +51 -0
- data/test/components/scm_test.rb +11 -0
- data/test/components/triggers_test.rb +81 -0
- data/test/components/wrappers_test.rb +19 -0
- data/test/driver_test.rb +47 -0
- data/test/job_test.rb +79 -0
- data/test/test_helper.rb +4 -0
- metadata +186 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
module Jujube
|
2
|
+
module Components
|
3
|
+
|
4
|
+
# Helper methods for creating trigger components.
|
5
|
+
module Triggers
|
6
|
+
|
7
|
+
# Specify a `pollscm` trigger for a job.
|
8
|
+
#
|
9
|
+
# See {http://ci.openstack.org/jenkins-job-builder/triggers.html#triggers.pollscm}.
|
10
|
+
#
|
11
|
+
# @param interval [String] The polling interval, using `cron` syntax.
|
12
|
+
# @return [Hash] The specification for the component.
|
13
|
+
def pollscm(interval)
|
14
|
+
{'pollscm' => interval}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Specify a `pollurl` trigger for a job.
|
18
|
+
#
|
19
|
+
# This trigger requires support in jenkins-job-builder that has not yet been merged.
|
20
|
+
# See {https://review.openstack.org/#/c/83524/} for the patch.
|
21
|
+
#
|
22
|
+
# `pollurl` can poll several URLs. Each URL specification is added
|
23
|
+
# in a nested configuration block using the {#url} method.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# job "pollurl-example" do |j|
|
27
|
+
# j.triggers << pollurl(cron: "CRON") do |urls|
|
28
|
+
# urls << url("URL", check_date: true) do |content|
|
29
|
+
# content << json("JSON_PATH1", "JSON_PATH2")
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# @param options [Hash] Top-level options for configuring the component.
|
35
|
+
# @yieldparam urls [Array] An array to which nested URL specifications should be
|
36
|
+
# added by the block.
|
37
|
+
# @return [Hash] The specification for the component.
|
38
|
+
def pollurl(options = {}, &block)
|
39
|
+
to_config("pollurl", nested_options(:urls, options, &block))
|
40
|
+
end
|
41
|
+
|
42
|
+
# @!group pollurl Helpers
|
43
|
+
|
44
|
+
# Configure a URL to poll in a {#pollurl} component.
|
45
|
+
#
|
46
|
+
# If you want to check for changes to the actual content returned from the URL,
|
47
|
+
# (the `check-content` setting), pass a block that configures the content-type
|
48
|
+
# specifications using one or more of {#simple}, {#json}, {#xml}, or {#text}.
|
49
|
+
#
|
50
|
+
# @param the_url [String] The URL to monitor.
|
51
|
+
# @param options [Hash] Top-level options for this URL.
|
52
|
+
# @yieldparam content_types [Array] An array to which nested content-type
|
53
|
+
# specifications should be added by the block.
|
54
|
+
# @return [Hash] The specification for the URL.
|
55
|
+
def url(the_url, options = {}, &block)
|
56
|
+
options = {url: the_url}.merge!(options)
|
57
|
+
canonicalize_options(nested_options(:check_content, options, &block))
|
58
|
+
end
|
59
|
+
|
60
|
+
# @!endgroup
|
61
|
+
|
62
|
+
# @!group pollurl Content Types
|
63
|
+
|
64
|
+
# Configure a simple content check inside a {#url} specification of a
|
65
|
+
# {#pollurl} component.
|
66
|
+
# @return [Hash] The specification for the content type.
|
67
|
+
def simple
|
68
|
+
{"simple" => true}
|
69
|
+
end
|
70
|
+
|
71
|
+
# Configure a JSON content check inside a {#url} specification of a
|
72
|
+
# {#pollurl} component.
|
73
|
+
#
|
74
|
+
# @param paths [String...] Zero or more JSONPath expressions. Only changes to
|
75
|
+
# the parts of the JSON response that match one of the `paths` will trigger a build.
|
76
|
+
# @return [Hash] The specification for the content type.
|
77
|
+
def json(*paths)
|
78
|
+
{"json" => paths}
|
79
|
+
end
|
80
|
+
|
81
|
+
# Configure an XML content check inside a {#url} specification of a
|
82
|
+
# {#pollurl} component.
|
83
|
+
#
|
84
|
+
# @param xpaths [String...] Zero or more XPath expressions. Only changes to
|
85
|
+
# the parts of the XML response that match one of the `xpaths` will trigger a build.
|
86
|
+
# @return [Hash] The specification for the content type.
|
87
|
+
def xml(*xpaths)
|
88
|
+
{"xml" => xpaths}
|
89
|
+
end
|
90
|
+
|
91
|
+
# Configure a text content check inside a {#url} specification of a
|
92
|
+
# {#pollurl} component.
|
93
|
+
#
|
94
|
+
# @param regexes [String...] Zero or more regular expressions. Only changes to
|
95
|
+
# the parts of the text response that match one of the `regexes` will trigger a build.
|
96
|
+
# @return [Hash] The specification for the content type.
|
97
|
+
def text(*regexes)
|
98
|
+
{"text" => regexes}
|
99
|
+
end
|
100
|
+
|
101
|
+
# @!endgroup
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Jujube
|
2
|
+
module Components
|
3
|
+
|
4
|
+
# Helper methods for creating wrapper components.
|
5
|
+
module Wrappers
|
6
|
+
extend Macros
|
7
|
+
|
8
|
+
# @!method timeout(options = {})
|
9
|
+
# Specify a `timeout` component for a job.
|
10
|
+
#
|
11
|
+
# See {http://ci.openstack.org/jenkins-job-builder/publishers.html#wrappers.timeout}.
|
12
|
+
#
|
13
|
+
# @param options [Hash] The configuration options for the component.
|
14
|
+
# @return [Hash] The specification for the component.
|
15
|
+
standard_component :timeout
|
16
|
+
|
17
|
+
# Specify a `timestamps` component for a job.
|
18
|
+
#
|
19
|
+
# See {http://ci.openstack.org/jenkins-job-builder/wrappers.html#wrappers.timestamps}.
|
20
|
+
#
|
21
|
+
# @return [Hash] The specification for the component.
|
22
|
+
def timestamps
|
23
|
+
'timestamps'
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require_relative "job_loader"
|
2
|
+
require_relative "job_file_generator"
|
3
|
+
require 'optparse'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module Jujube
|
8
|
+
|
9
|
+
# The command-line driver for the Jujube application.
|
10
|
+
class Driver
|
11
|
+
|
12
|
+
# Run the Jujube application.
|
13
|
+
def self.run
|
14
|
+
self.new.run
|
15
|
+
end
|
16
|
+
|
17
|
+
# Initialize the driver.
|
18
|
+
#
|
19
|
+
# @param loader [JobLoader] Loads jobs from user-specified files and/or directories.
|
20
|
+
# @param generator [JobFileGenerator] Generates the jenkins-job-builder YAML file from
|
21
|
+
# any jobs that have been loaded.
|
22
|
+
def initialize(loader = JobLoader.new, generator = JobFileGenerator.new)
|
23
|
+
@loader = loader
|
24
|
+
@generator = generator
|
25
|
+
end
|
26
|
+
|
27
|
+
# Run the Jujube application.
|
28
|
+
#
|
29
|
+
# @param argv [Array] The command-line arguments that control the application.
|
30
|
+
def run(argv = ARGV)
|
31
|
+
argv = adjusted_for_jruby(argv)
|
32
|
+
options = handle_options(argv)
|
33
|
+
jobs = @loader.load_jobs(*options.paths || Pathname.getwd)
|
34
|
+
@generator.generate(jobs, options.output || Pathname.new("jobs.yml"))
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Parse the command-line options.
|
40
|
+
#
|
41
|
+
# @param argv [Array] The command-line options.
|
42
|
+
# @return [OpenStruct] The options specified on the command-line. May include
|
43
|
+
# `paths`, a list of `Pathname`s to load and `output`, the `Pathname` of the
|
44
|
+
# output file to generate.
|
45
|
+
def handle_options(argv)
|
46
|
+
options = OpenStruct.new
|
47
|
+
|
48
|
+
OptionParser.new do |opts|
|
49
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options] paths..."
|
50
|
+
|
51
|
+
opts.on('-o', '--output FILENAME', 'Specify output file (default = "jobs.yml")') do |file|
|
52
|
+
options.output = Pathname.new(file)
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-h', '--help', "Display this screen") do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end.parse!(argv)
|
60
|
+
|
61
|
+
options.paths = argv.map { |file| Pathname.new(file) } unless argv.empty?
|
62
|
+
|
63
|
+
options
|
64
|
+
end
|
65
|
+
|
66
|
+
# Adjust the command-line arguments for running acceptance tests under JRuby.
|
67
|
+
#
|
68
|
+
# When running the acceptance tests under JRuby, an extra '{}' argument
|
69
|
+
# is passed in ARGV. We remove it here so that it doesn't affect the
|
70
|
+
# rest of the program logic.
|
71
|
+
#
|
72
|
+
# See {https://github.com/jruby/jruby/issues/1290}.
|
73
|
+
def adjusted_for_jruby(argv)
|
74
|
+
argv.pop if argv.last == "{}"
|
75
|
+
argv
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/jujube/dsl.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Jujube
|
2
|
+
# The top-level DSL to be used when defining jobs.
|
3
|
+
module DSL
|
4
|
+
include Jujube::Components
|
5
|
+
|
6
|
+
# Define a new Jenkins job.
|
7
|
+
#
|
8
|
+
# Takes a configuration block that is used to configure the job by
|
9
|
+
# setting attribute values and adding components to the various sections
|
10
|
+
# of the job.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# job "my-job" do |j|
|
14
|
+
# j.description = "This is my job." # Specify attributes
|
15
|
+
# j.quiet_period = 5
|
16
|
+
#
|
17
|
+
# j.axes << slave(:arch, %w{i386 amd64}) # Add components to sections
|
18
|
+
#
|
19
|
+
# j.scm << git(url: "https://example.com/git/my-project", branches: %w{master dev})
|
20
|
+
#
|
21
|
+
# j.triggers << pollscm("@hourly")
|
22
|
+
#
|
23
|
+
# j.wrappers << timeout(type: 'elastic', elastic_percentage: 150, elastic_default_timeout: 5, fail: true)
|
24
|
+
# j.wrappers << timestamps
|
25
|
+
#
|
26
|
+
# j.builders << shell("bundle && bundle exec rake deploy")
|
27
|
+
#
|
28
|
+
# j.publishers << email_ext(recipients: %w{me@example.com you@example.com})
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# @param name [String] The name of the job as will be shown by Jenkins.
|
32
|
+
# @yieldparam job [Job] The job being created.
|
33
|
+
def job(name, &block)
|
34
|
+
Job.new(name, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Extend the main object with the DSL commands. This allows top-level
|
41
|
+
# calls to the DSL functions to work from a job file without polluting
|
42
|
+
# the object inheritance tree.
|
43
|
+
self.extend Jujube::DSL
|
data/lib/jujube/job.rb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
require_relative "macros"
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Jujube
|
5
|
+
# Models a single Jenkins job.
|
6
|
+
class Job
|
7
|
+
extend Macros
|
8
|
+
|
9
|
+
# Initialize the job.
|
10
|
+
#
|
11
|
+
# Takes a configuration block for adding atributes and sections to the job.
|
12
|
+
# The configuration block is passed the new `Job` as a parameter.
|
13
|
+
#
|
14
|
+
# @param job_name [String] The name of the job. Will be the name as seen in Jenkins.
|
15
|
+
# @yieldparam self [Job] Passes itself to the configuration block.
|
16
|
+
def initialize(job_name)
|
17
|
+
@config = {}
|
18
|
+
self.name = job_name
|
19
|
+
|
20
|
+
yield(self) if block_given?
|
21
|
+
|
22
|
+
self.class.register(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
# @!group Attributes
|
26
|
+
|
27
|
+
# @!attribute name
|
28
|
+
# The name of the job - will be the name as seen in Jenkins.
|
29
|
+
#
|
30
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
attribute :name
|
34
|
+
|
35
|
+
# @!attribute project_type
|
36
|
+
# The type of job. This normally does not need to be specified, as it
|
37
|
+
# will be inferred as `matrix` if any `axes` are added.
|
38
|
+
#
|
39
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
attribute :project_type
|
43
|
+
|
44
|
+
# @!attribute description
|
45
|
+
# The description of the job.
|
46
|
+
#
|
47
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
48
|
+
#
|
49
|
+
# @return [String]
|
50
|
+
attribute :description
|
51
|
+
|
52
|
+
# @!attribute node
|
53
|
+
# The Jenkins node or named group where the job should be run.
|
54
|
+
#
|
55
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
56
|
+
#
|
57
|
+
# @return [String]
|
58
|
+
attribute :node
|
59
|
+
|
60
|
+
# @!attribute block_upstream
|
61
|
+
# `true` if this job should block while upstream jobs are running.
|
62
|
+
#
|
63
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
64
|
+
#
|
65
|
+
# @return [Boolean]
|
66
|
+
attribute :block_upstream
|
67
|
+
|
68
|
+
# @!attribute block_downstream
|
69
|
+
# `true` if this job should block while downstream jobs are running.
|
70
|
+
#
|
71
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
72
|
+
#
|
73
|
+
# @return [Booelan]
|
74
|
+
attribute :block_downstream
|
75
|
+
|
76
|
+
# @!attribute quiet_period
|
77
|
+
# Number of seconds to wait between consecutive runs of the job.
|
78
|
+
#
|
79
|
+
# See {http://ci.openstack.org/jenkins-job-builder/general.html}.
|
80
|
+
#
|
81
|
+
# @return [Fixnum]
|
82
|
+
attribute :quiet_period
|
83
|
+
|
84
|
+
# @!endgroup
|
85
|
+
|
86
|
+
# @!group Sections
|
87
|
+
|
88
|
+
# @!method axes
|
89
|
+
# The matrix axes for the job.
|
90
|
+
#
|
91
|
+
# Add axes in the job's configuration block using helper methods defined in
|
92
|
+
# {Components::Parameters}.
|
93
|
+
#
|
94
|
+
# See {http://ci.openstack.org/jenkins-job-builder/project_matrix.html}.
|
95
|
+
#
|
96
|
+
# @return [Array]
|
97
|
+
section :axes
|
98
|
+
|
99
|
+
# @!method scm
|
100
|
+
# The SCMs for the job.
|
101
|
+
#
|
102
|
+
# Add SCMs in the job's configuration block using helper methods defined in
|
103
|
+
# {Components::Scm}.
|
104
|
+
#
|
105
|
+
# See {http://ci.openstack.org/jenkins-job-builder/scm.html}.
|
106
|
+
#
|
107
|
+
# @return [Array]
|
108
|
+
section :scm
|
109
|
+
|
110
|
+
# @!method triggers
|
111
|
+
# The triggers for the job.
|
112
|
+
#
|
113
|
+
# Add triggers in the job's configuration block using helper methods defined in
|
114
|
+
# {Components::Triggers}.
|
115
|
+
#
|
116
|
+
# See {http://ci.openstack.org/jenkins-job-builder/triggers.html}.
|
117
|
+
#
|
118
|
+
# @return [Array]
|
119
|
+
section :triggers
|
120
|
+
|
121
|
+
# @!method wrappers
|
122
|
+
# The wrappers for the job.
|
123
|
+
#
|
124
|
+
# Add wrappers in the job's configuration block using helper methods defined in
|
125
|
+
# {Components::Wrappers}.
|
126
|
+
#
|
127
|
+
# See {http://ci.openstack.org/jenkins-job-builder/wrappers.html}.
|
128
|
+
#
|
129
|
+
# @return [Array]
|
130
|
+
section :wrappers
|
131
|
+
|
132
|
+
# @!method builders
|
133
|
+
# The builders for the job.
|
134
|
+
#
|
135
|
+
# Add builders in the job's configuration block using helper methods defined in
|
136
|
+
# {Components::Builders}.
|
137
|
+
#
|
138
|
+
# See {http://ci.openstack.org/jenkins-job-builder/builders.html}.
|
139
|
+
#
|
140
|
+
# @return [Array]
|
141
|
+
section :builders
|
142
|
+
|
143
|
+
# @!method publishers
|
144
|
+
# The publishers for the job.
|
145
|
+
#
|
146
|
+
# Add publishers in the job's configuration block using helper methods defined in
|
147
|
+
# {Components::Publishers}.
|
148
|
+
#
|
149
|
+
# See {http://ci.openstack.org/jenkins-job-builder/publishers.html}.
|
150
|
+
#
|
151
|
+
# @return [Array]
|
152
|
+
section :publishers
|
153
|
+
|
154
|
+
# @!method notifications
|
155
|
+
# The notifications for the job.
|
156
|
+
#
|
157
|
+
# Add notifications in the job's configuration block using helper methods defined in
|
158
|
+
# {Components::Notifications}.
|
159
|
+
#
|
160
|
+
# See {http://ci.openstack.org/jenkins-job-builder/notifications.html}.
|
161
|
+
#
|
162
|
+
# @return [Array]
|
163
|
+
section :notifications
|
164
|
+
|
165
|
+
# @!endgroup
|
166
|
+
|
167
|
+
# Generate a YAML representation of the `Job`.
|
168
|
+
def to_yaml(*args)
|
169
|
+
to_h.to_yaml(*args)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Generate a `Hash` repsentation of the `Job`.
|
173
|
+
def to_h
|
174
|
+
infer_project_type
|
175
|
+
{'job' => config}
|
176
|
+
end
|
177
|
+
|
178
|
+
# Keep track of all `Job`s defined during the execution of the passed block.
|
179
|
+
#
|
180
|
+
# This is used during job loading so that no extra syntax is required in the job
|
181
|
+
# definition files for registering jobs and so that it is possible to create jobs
|
182
|
+
# for testing purposes without having them registered anywhere.
|
183
|
+
#
|
184
|
+
# @return [Array<Job>] Returns a list of all `Job`s defined within the block.
|
185
|
+
def self.all_defined_during
|
186
|
+
@registry = []
|
187
|
+
yield
|
188
|
+
@registry
|
189
|
+
ensure
|
190
|
+
@registry = []
|
191
|
+
end
|
192
|
+
|
193
|
+
# Register a `Job` in a registry.
|
194
|
+
#
|
195
|
+
# If no registry is active, this is a no-op.
|
196
|
+
#
|
197
|
+
# @param job [Job] The job to register.
|
198
|
+
def self.register(job)
|
199
|
+
@registry << job if @registry
|
200
|
+
end
|
201
|
+
|
202
|
+
# The job configuration.
|
203
|
+
#
|
204
|
+
# All configuration is stored internally as a `Hash` with `String` keys in
|
205
|
+
# canonical jenkins-job-builder format. This format can readily be converted
|
206
|
+
# to the YAML needed by jenkins-job-builder.
|
207
|
+
attr_reader :config
|
208
|
+
private :config
|
209
|
+
|
210
|
+
private
|
211
|
+
|
212
|
+
# Set the {#project_type} to `matrix` if any `axes` have been specified.
|
213
|
+
def infer_project_type
|
214
|
+
self.project_type = "matrix" if has_axes?
|
215
|
+
end
|
216
|
+
|
217
|
+
# Have any `axes` been specified?
|
218
|
+
#
|
219
|
+
# Take care to not lazily-initialize an `"axes"` key when checking.
|
220
|
+
#
|
221
|
+
# @return [Boolean] `true` if any `axes` have been defined; `false` otherwise.
|
222
|
+
def has_axes?
|
223
|
+
config.has_key?("axes") && !axes.empty?
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|