jujube 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,17 @@
|
|
1
|
+
module Jujube
|
2
|
+
# Generate a YAML file suitable for jenkins-job-builder.
|
3
|
+
class JobFileGenerator
|
4
|
+
|
5
|
+
# Generate a jenkins-job-builder YAML file for a list of jobs.
|
6
|
+
#
|
7
|
+
# @param jobs [Array] The job definitions to include in the output.
|
8
|
+
# @param output [Pathname] The output file to generate. Any intermediate
|
9
|
+
# directories are created automatically.
|
10
|
+
def generate(jobs, output)
|
11
|
+
output.dirname.mkpath
|
12
|
+
output.open("w") do |io|
|
13
|
+
jobs.map(&:to_h).to_yaml(io)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Jujube
|
2
|
+
|
3
|
+
# Loads job definitions from a set of files and/or directories.
|
4
|
+
class JobLoader
|
5
|
+
|
6
|
+
# Load job definitions from one or more files and/or directories.
|
7
|
+
#
|
8
|
+
# The job definition files are loaded as Ruby files; it is expected that
|
9
|
+
# will make use of the {#job} DSL function, but they can contain other
|
10
|
+
# Ruby code as well.
|
11
|
+
#
|
12
|
+
# @param pathnames [Pathname...] The file or directory names containing
|
13
|
+
# the job definitions.
|
14
|
+
def load_jobs(*pathnames)
|
15
|
+
Job.all_defined_during do
|
16
|
+
pathnames.each do |path|
|
17
|
+
load_one(path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Load jobs from a single file or directory
|
25
|
+
#
|
26
|
+
# @param path [Pathname] The file or directory to load from.
|
27
|
+
def load_one(path)
|
28
|
+
if path.directory?
|
29
|
+
load_directory(path)
|
30
|
+
else
|
31
|
+
load_file(path)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Load jobs from all (recursive) files in a directory.
|
36
|
+
#
|
37
|
+
# @param path [Pathname] The directory to search.
|
38
|
+
def load_directory(path)
|
39
|
+
path.each_child do |child|
|
40
|
+
load_one(child)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Load jobs from a single file.
|
45
|
+
#
|
46
|
+
# @param path [Pathname] The file to load.
|
47
|
+
def load_file(path)
|
48
|
+
load(path)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Jujube
|
2
|
+
|
3
|
+
# Macros for defining methods that make it easier to specify the parts
|
4
|
+
# a job.
|
5
|
+
module Macros
|
6
|
+
include Jujube::Utils
|
7
|
+
|
8
|
+
# A macro that defines an attribute of a job.
|
9
|
+
# It generates a reader and a writer for the attribute.
|
10
|
+
#
|
11
|
+
# @param attribute_name [Symbol] The name of the attribute.
|
12
|
+
def attribute(attribute_name)
|
13
|
+
canonical_name = canonicalize(attribute_name)
|
14
|
+
|
15
|
+
define_method attribute_name do
|
16
|
+
config[canonical_name]
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method "#{attribute_name}=".to_sym do |value|
|
20
|
+
config[canonical_name] = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# A macro that defines a section of a job.
|
25
|
+
# It generates a method that returns the `Array` of components
|
26
|
+
# in that section.
|
27
|
+
#
|
28
|
+
# @param section_name [Symbol] The name of the section.
|
29
|
+
def section(section_name)
|
30
|
+
define_method section_name do
|
31
|
+
config[section_name.to_s] ||= []
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/jujube/utils.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Jujube
|
2
|
+
# Private utility methods.
|
3
|
+
module Utils
|
4
|
+
private
|
5
|
+
|
6
|
+
# Convert a hash key into the canonical format required by
|
7
|
+
# jenkins-job-builder. Keys must be strings and use
|
8
|
+
# dashes (`-`) instead of underscores (`_`).
|
9
|
+
#
|
10
|
+
# @param key [Symbol] The key to canonicalize.
|
11
|
+
# @return [String] The key in canonical format.
|
12
|
+
def canonicalize(key)
|
13
|
+
key.to_s.gsub("_", "-")
|
14
|
+
end
|
15
|
+
|
16
|
+
# Ensure that all of the keys in an options hash are in
|
17
|
+
# canonical jenkins-job-builder format. This method only
|
18
|
+
# looks at the top-level keys; it assumes that any
|
19
|
+
# nested hashes have already been {#canonicalize}d.
|
20
|
+
#
|
21
|
+
# @param options [Hash] The options to canonicalize.
|
22
|
+
# @return [Hash] The options in canonical format.
|
23
|
+
def canonicalize_options(options)
|
24
|
+
Hash[options.map { |k, v| [canonicalize(k), v] }]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
class ParametersTest < Minitest::Test
|
4
|
+
include Jujube::Components
|
5
|
+
|
6
|
+
def test_label_expression_axis
|
7
|
+
expected = {"axis" => {"type" => "label-expression", "name" => "arch", "values" => %w{i386 amd64}}}
|
8
|
+
assert_equal(expected, label_expression(:arch, %w{i386 amd64}))
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_slave_axis
|
12
|
+
expected = {"axis" => {"type" => "slave", "name" => "arch", "values" => %w{i386 amd64}}}
|
13
|
+
assert_equal(expected, slave(:arch, %w{i386 amd64}))
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
class PublishersTest < Minitest::Test
|
4
|
+
include Jujube::Components
|
5
|
+
|
6
|
+
def test_email_ext
|
7
|
+
assert_equal("email-ext", email_ext)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_email_ext_with_recipients
|
11
|
+
expected = {"email-ext" => {"recipients" => "fred, barney"}}
|
12
|
+
assert_equal(expected, email_ext(recipients: %w{fred barney}.join(", ")))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_ircbot
|
16
|
+
expected = {"ircbot" => {"notify-start" => true}}
|
17
|
+
assert_equal(expected, ircbot(notify_start: true))
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_junit
|
21
|
+
expected = {"junit" => {"results" => "RESULTS", "keep-long-stdio" => false}}
|
22
|
+
assert_equal(expected, junit(results: "RESULTS", keep_long_stdio: false))
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_archive
|
26
|
+
expected = {"archive" => {"artifacts" => "ARTIFACTS", "latest-only" => true, "allow-empty" => true}}
|
27
|
+
assert_equal(expected, archive(artifacts: "ARTIFACTS", latest_only: true, allow_empty: true))
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_cppcheck
|
31
|
+
expected = {"cppcheck" => {"pattern" => "PATTERN"}}
|
32
|
+
assert_equal(expected, cppcheck(pattern: "PATTERN"))
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_xunit
|
36
|
+
expected = {"xunit" =>
|
37
|
+
{"types" =>
|
38
|
+
[{"unittest" => {"pattern" => "PATTERN", "deleteoutput" => false}}]
|
39
|
+
}
|
40
|
+
}
|
41
|
+
actual = xunit do |types|
|
42
|
+
types << unittest(pattern: "PATTERN", deleteoutput: false)
|
43
|
+
end
|
44
|
+
assert_equal(expected, actual)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_trigger
|
48
|
+
expected = {"trigger" => {"project" => "PROJECT"}}
|
49
|
+
assert_equal(expected, trigger(project: "PROJECT"))
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
class ScmTest < Minitest::Test
|
4
|
+
include Jujube::Components
|
5
|
+
|
6
|
+
def test_git
|
7
|
+
expected = {"git" => {"url" => "URL", "branches" => ["master", "dev"], "wipe-workspace" => false}}
|
8
|
+
actual = git(url: "URL", branches: %w{master dev}, wipe_workspace: false)
|
9
|
+
assert_equal(expected, actual)
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
class TriggersTest < Minitest::Test
|
4
|
+
include Jujube::Components
|
5
|
+
|
6
|
+
def test_pollscm
|
7
|
+
assert_equal({"pollscm" => "INTERVAL"}, pollscm("INTERVAL"))
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_pollurl_with_no_content_checks
|
11
|
+
expected = {"pollurl" =>
|
12
|
+
{"cron" => "CRON",
|
13
|
+
"urls" => [{"url" => "URL", "check-date" => true}]
|
14
|
+
}
|
15
|
+
}
|
16
|
+
actual = pollurl(cron: "CRON") do |urls|
|
17
|
+
urls << url("URL", check_date: true)
|
18
|
+
end
|
19
|
+
assert_equal(expected, actual)
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_pollurl_with_simple_content
|
23
|
+
expected = {"pollurl" =>
|
24
|
+
{"urls" =>
|
25
|
+
[{"url" => "URL",
|
26
|
+
"check-content" => [{"simple" => true}]}]
|
27
|
+
}
|
28
|
+
}
|
29
|
+
actual = pollurl do |urls|
|
30
|
+
urls << url("URL") do |content|
|
31
|
+
content << simple
|
32
|
+
end
|
33
|
+
end
|
34
|
+
assert_equal(expected, actual)
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_pollurl_with_json_content
|
38
|
+
expected = {"pollurl" =>
|
39
|
+
{"urls" =>
|
40
|
+
[{"url" => "URL",
|
41
|
+
"check-content" => [{"json" => %w{JSON_PATH1 JSON_PATH2}}]}]
|
42
|
+
}
|
43
|
+
}
|
44
|
+
actual = pollurl do |urls|
|
45
|
+
urls << url("URL") do |content|
|
46
|
+
content << json("JSON_PATH1", "JSON_PATH2")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
assert_equal(expected, actual)
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_pollurl_with_xml_content
|
53
|
+
expected = {"pollurl" =>
|
54
|
+
{"urls" =>
|
55
|
+
[{"url" => "URL",
|
56
|
+
"check-content" => [{"xml" => %w{XPATH1 XPATH2}}]}]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
actual = pollurl do |urls|
|
60
|
+
urls << url("URL") do |content|
|
61
|
+
content << xml("XPATH1", "XPATH2")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
assert_equal(expected, actual)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_pollurl_with_text_content
|
68
|
+
expected = {"pollurl" =>
|
69
|
+
{"urls" =>
|
70
|
+
[{"url" => "URL",
|
71
|
+
"check-content" => [{"text" => %w{REGEX1 REGEX2}}]}]
|
72
|
+
}
|
73
|
+
}
|
74
|
+
actual = pollurl do |urls|
|
75
|
+
urls << url("URL") do |content|
|
76
|
+
content << text("REGEX1", "REGEX2")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
assert_equal(expected, actual)
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "../test_helper"
|
2
|
+
|
3
|
+
class WrappersTest < Minitest::Test
|
4
|
+
include Jujube::Components
|
5
|
+
|
6
|
+
def test_timestamps
|
7
|
+
assert_equal('timestamps', timestamps)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_timeout
|
11
|
+
expected = {'timeout' => {'type' => 'elastic', 'fail' => true}}
|
12
|
+
assert_equal(expected, timeout(type: 'elastic', fail: true))
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_canonicalizes_timeout_keys
|
16
|
+
expected = {'timeout' => {'elastic-percentage' => 150, 'elastic-default-timeout' => 3}}
|
17
|
+
assert_equal(expected, timeout(elastic_percentage: 150, elastic_default_timeout: 3))
|
18
|
+
end
|
19
|
+
end
|
data/test/driver_test.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
require "flexmock"
|
3
|
+
|
4
|
+
class DriverTest < Minitest::Test
|
5
|
+
include FlexMock::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@jobs = [Object.new, Object.new]
|
9
|
+
@loader = flexmock("loader", :on, Jujube::JobLoader)
|
10
|
+
@generator = flexmock("generator", :on, Jujube::JobFileGenerator)
|
11
|
+
@driver = Jujube::Driver.new(@loader, @generator)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_loads_all_files_in_current_directory_by_default
|
15
|
+
@driver.run([])
|
16
|
+
assert_spy_called(@loader, :load_jobs, Pathname.getwd)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_loads_single_file
|
20
|
+
@driver.run(%w{test.job})
|
21
|
+
assert_spy_called(@loader, :load_jobs, Pathname.new("test.job"))
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_loads_multiple_files
|
25
|
+
@driver.run(%w{job1.job job2.job})
|
26
|
+
assert_spy_called(@loader, :load_jobs, Pathname.new("job1.job"), Pathname.new("job2.job"))
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_generates_job_files
|
30
|
+
@loader.should_receive(:load_jobs => @jobs)
|
31
|
+
@driver.run([])
|
32
|
+
assert_spy_called(@generator, :generate, @jobs, Pathname.new("jobs.yml"))
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_generates_into_output_file
|
36
|
+
@loader.should_receive(:load_jobs => @jobs)
|
37
|
+
@driver.run(%w{-o OUTPUT_FILE})
|
38
|
+
assert_spy_called(@generator, :generate, @jobs, Pathname.new("OUTPUT_FILE"))
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_handles_mix_of_arguments
|
42
|
+
@loader.should_receive(:load_jobs => @jobs)
|
43
|
+
@driver.run(%w{job1.job job2.job -o OUTPUT_FILE})
|
44
|
+
assert_spy_called(@loader, :load_jobs, Pathname.new("job1.job"), Pathname.new("job2.job"))
|
45
|
+
assert_spy_called(@generator, :generate, @jobs, Pathname.new("OUTPUT_FILE"))
|
46
|
+
end
|
47
|
+
end
|
data/test/job_test.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class JobTest < Minitest::Test
|
4
|
+
include Jujube
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@job = Job.new("testJob")
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_knows_name
|
11
|
+
assert_equal("testJob", @job.name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_includes_name
|
15
|
+
assert_yaml_matches("name: testJob")
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_includes_description
|
19
|
+
@job.description = "DESCRIPTION"
|
20
|
+
assert_yaml_matches("description: DESCRIPTION")
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_includes_node
|
24
|
+
@job.node = "NODE"
|
25
|
+
assert_yaml_matches("node: NODE")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_includes_block_upstream
|
29
|
+
@job.block_upstream = true
|
30
|
+
assert_yaml_matches("block-upstream: true")
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_includes_block_downstream
|
34
|
+
@job.block_downstream = true
|
35
|
+
assert_yaml_matches("block-downstream: true")
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_includes_quiet_period
|
39
|
+
@job.quiet_period = 42
|
40
|
+
assert_yaml_matches("quiet-period: 42")
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_includes_single_item_section
|
44
|
+
@job.triggers << "TRIGGER"
|
45
|
+
assert_yaml_matches("triggers:\s*\n - TRIGGER")
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_includes_multi_item_section
|
49
|
+
@job.wrappers << "FIRST" << "SECOND"
|
50
|
+
assert_yaml_matches("wrappers:\s*\n - FIRST\n - SECOND")
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_infers_matrix_project_type_when_axes_specified
|
54
|
+
@job.axes << "AXIS"
|
55
|
+
assert_yaml_matches("project-type: matrix")
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_renames_scms_to_scm
|
59
|
+
@job.scm << "SCM"
|
60
|
+
assert_yaml_matches("scm:\s*\n - SCM")
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_self_registers_with_active_registry
|
64
|
+
job1 = nil
|
65
|
+
job2 = nil
|
66
|
+
jobs = Job.all_defined_during do
|
67
|
+
job1 = Job.new("job1")
|
68
|
+
job2 = Job.new("job2")
|
69
|
+
end
|
70
|
+
assert_equal([job1, job2], jobs)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def assert_yaml_matches(snippet)
|
76
|
+
expected = %r{^job:\s*$.*^ #{snippet}$}m
|
77
|
+
assert_match(expected, @job.to_yaml)
|
78
|
+
end
|
79
|
+
end
|