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,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
|