jujube 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +9 -0
  4. data/.yardopts +4 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +22 -0
  7. data/README.md +180 -0
  8. data/Rakefile +15 -0
  9. data/acceptance/fixtures/endToEnd/endToEnd.job +43 -0
  10. data/acceptance/fixtures/endToEnd/expected.yml +80 -0
  11. data/acceptance/fixtures/multipleFiles/job1.job +1 -0
  12. data/acceptance/fixtures/multipleFiles/job2.job +1 -0
  13. data/acceptance/fixtures/multipleJobs/test.job +2 -0
  14. data/acceptance/fixtures/nestedDirectories/job1.job +1 -0
  15. data/acceptance/fixtures/nestedDirectories/nested/job2.job +1 -0
  16. data/acceptance/fixtures/singleJob/test.job +3 -0
  17. data/acceptance/jujube/acceptance_test.rb +47 -0
  18. data/acceptance/jujube/end_to_end_test.rb +13 -0
  19. data/acceptance/jujube/usage_test.rb +90 -0
  20. data/bin/jujube +5 -0
  21. data/jujube.gemspec +29 -0
  22. data/lib/jujube.rb +6 -0
  23. data/lib/jujube/components.rb +23 -0
  24. data/lib/jujube/components/builders.rb +19 -0
  25. data/lib/jujube/components/helpers.rb +40 -0
  26. data/lib/jujube/components/macros.rb +31 -0
  27. data/lib/jujube/components/notifications.rb +11 -0
  28. data/lib/jujube/components/parameters.rb +50 -0
  29. data/lib/jujube/components/publishers.rb +97 -0
  30. data/lib/jujube/components/scm.rb +19 -0
  31. data/lib/jujube/components/triggers.rb +105 -0
  32. data/lib/jujube/components/wrappers.rb +28 -0
  33. data/lib/jujube/driver.rb +78 -0
  34. data/lib/jujube/dsl.rb +43 -0
  35. data/lib/jujube/job.rb +226 -0
  36. data/lib/jujube/job_file_generator.rb +17 -0
  37. data/lib/jujube/job_loader.rb +51 -0
  38. data/lib/jujube/macros.rb +35 -0
  39. data/lib/jujube/utils.rb +27 -0
  40. data/lib/jujube/version.rb +5 -0
  41. data/test/components/builders_test.rb +10 -0
  42. data/test/components/parameters_test.rb +15 -0
  43. data/test/components/publishers_test.rb +51 -0
  44. data/test/components/scm_test.rb +11 -0
  45. data/test/components/triggers_test.rb +81 -0
  46. data/test/components/wrappers_test.rb +19 -0
  47. data/test/driver_test.rb +47 -0
  48. data/test/job_test.rb +79 -0
  49. data/test/test_helper.rb +4 -0
  50. 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
@@ -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,5 @@
1
+ # The main namespace for the Jujube gem.
2
+ module Jujube
3
+ # The current version of the Jujube gem.
4
+ VERSION = "0.5.0"
5
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "../test_helper"
2
+
3
+ class BuildersTest < Minitest::Test
4
+ include Jujube::Components
5
+
6
+ def test_shell
7
+ expected = {'shell' => 'COMMAND'}
8
+ assert_equal(expected, shell('COMMAND'))
9
+ end
10
+ 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
@@ -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