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.
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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80df34de2a96d739460c44eaa0eda01f6f6e7ea5
4
+ data.tar.gz: 4d8b6e80aa1ad6a483465f11ae554a9ec1552352
5
+ SHA512:
6
+ metadata.gz: 52a68f97e65f9ed50feef8e13ccd5a57831f62198da01b4c4f210ba7b15c25347b6488d0a5e97a75e31ed35ba2a1b6fbb1eeb17e767e9c002bbb6fd3428d947b
7
+ data.tar.gz: 6c5a4a2ae6f04d5c85e0ac3af8f84f83c18229698c51543a36da925a7a4e98db1826c09f758468ecec8c03e210626e1a3cfca8f79096d27145810ec5f8ce11fd
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
6
+ - 2.1.0
7
+ - jruby-19mode
8
+ - rbx
9
+ script: "bundle exec rake"
data/.yardopts ADDED
@@ -0,0 +1,4 @@
1
+ --markup markdown
2
+ -
3
+ README.md
4
+ LICENSE
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jujube.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Randy Coulman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # Jujube
2
+
3
+ [![Build Status](https://travis-ci.org/randycoulman/jujube.svg?branch=master)](https://travis-ci.org/randycoulman/jujube)
4
+ [![Code Climate](https://codeclimate.com/github/randycoulman/jujube.png)](https://codeclimate.com/github/randycoulman/jujube)
5
+
6
+ **Jujube** is a Ruby front-end for
7
+ [jenkins-job-builder](https://github.com/openstack-infra/jenkins-job-builder).
8
+
9
+ jenkins-job-builder allows you to specify Jenkins jobs in YAML and then creates or
10
+ updates the jobs in a running Jenkins instance. It provides some tools for removing
11
+ duplication from job definitions, but sometimes more abstraction capability is needed.
12
+ **Jujube** provides that capability.
13
+
14
+ Using **Jujube**, you can specify Jenkins jobs using a Ruby DSL (domain-specific language).
15
+ **Jujube** then generates the YAML input needed by jenkins-job-builder. **Jujube** also makes
16
+ it easy to define methods that act as job templates or pre-configured components.
17
+
18
+ ## Installation
19
+
20
+ Add this line to your application's Gemfile:
21
+
22
+ gem 'jujube'
23
+
24
+ And then execute:
25
+
26
+ $ bundle
27
+
28
+ Or install it yourself as:
29
+
30
+ $ gem install jujube
31
+
32
+ ## Usage Instructions
33
+
34
+ ### Running Jujube
35
+
36
+ To run **Jujube** and convert one or more job definitions to YAML for use by jenkins-job-builder:
37
+
38
+ ```
39
+ jujube [--output output-file] file-or-directory*
40
+ ```
41
+
42
+ **Jujube** will load all of the specified files (or all of the files in the specified directories)
43
+ and generate a single YAML file containing all of the jenkins-job-builder job definitions.
44
+
45
+ If the `--output` (or `-o` for short) option is provided, the job definitions will be generated into
46
+ the specified file. Any intermediate directories that do not already exist will be created
47
+ automatically.
48
+
49
+ If no output filename is specified, then a file named `jobs.yml` in the current working directory
50
+ will be used.
51
+
52
+ ### Defining Jobs
53
+
54
+ To define a Jenkins job using **Jujube**, use the `job` DSL function. The most basic job definition
55
+ is just a job name:
56
+
57
+ ```ruby
58
+ job "my-job"
59
+ ```
60
+
61
+ Such a job does absolutely nothing, so you will most likely want to provide a configuration block as
62
+ well:
63
+
64
+ ```ruby
65
+ job "my-job" do |j|
66
+ # Job configuration goes here...
67
+ end
68
+ ```
69
+
70
+ In the job configuration block, you can specify attributes for the job and add components to sections.
71
+ For example:
72
+
73
+ ```ruby
74
+ job "my-job" do |j|
75
+ j.description = "This is my job." # Specify attributes
76
+ j.quiet_period = 5
77
+
78
+ j.axes << slave(:arch, %w{i386 amd64}) # Add components to sections
79
+
80
+ j.scm << git(url: "https://example.com/git/my-project", branches: %w{master dev})
81
+
82
+ j.triggers << pollscm("@hourly")
83
+
84
+ j.wrappers << timeout(type: 'elastic', elastic_percentage: 150, elastic_default_timeout: 5, fail: true)
85
+ j.wrappers << timestamps
86
+
87
+ j.builders << shell("bundle && bundle exec rake deploy")
88
+
89
+ j.publishers << email_ext(recipients: %w{me@example.com you@example.com})
90
+ end
91
+ ```
92
+
93
+ The following job attributes are supported:
94
+
95
+ * `description`
96
+ * `project_type` -- doesn't normally need to be specified. If any `axes` are added to the
97
+ job, then `project_type` is automatically inferred to be `matrix`
98
+ * `description`
99
+ * `node`
100
+ * `block_upstream`
101
+ * `block_downstream`
102
+ * `quiet_period`
103
+
104
+ The following sections and components are supported:
105
+
106
+ * `axes`: (`label_expression`, `slave`)
107
+ * `scm`: (`git`)
108
+ * `triggers`: (`pollscm`)
109
+ * `wrappers`: (`timeout`, `timestamps`)
110
+ * `builders`: (`shell`)
111
+ * `publishers`: (`archive`, `cppcheck`, `email_ext`, `ircbot`, `junit`, `trigger`, `xunit`)
112
+ * `notifications`: None defined yet
113
+
114
+ See [the end-to-end example job](examples/fixtures/endToEnd/endToEnd.job) for example
115
+ uses of all of the supported attributes, sections, and components.
116
+
117
+ In general, component options are typically specified as standard Ruby hashes. The option
118
+ names are the same as in the
119
+ [jenkins-job-builder documentation](http://ci.openstack.org/jenkins-job-builder/),
120
+ except that you need to use underscores (`_`) instead of dashes (`-`) in the option names.
121
+
122
+ ### Helper Methods
123
+
124
+ In your own projects, you can define helper methods that pre-configure jobs or components.
125
+ Simply `require` the Ruby files containing your helper methods into your job files and use
126
+ them just like the **Jujube** supplied methods. For example:
127
+
128
+ ```ruby
129
+ def template_job(name, description)
130
+ job name do |j|
131
+ j.description = description
132
+ j.quiet_period = 5
133
+ j.scm << standard_git(name)
134
+ j.triggers << pollscm("@hourly")
135
+ j.wrappers << timestamps
136
+
137
+ yield j if block_given?
138
+ end
139
+ end
140
+
141
+ def standard_git(repo_name)
142
+ git(url: "https://example.com/git/#{repo_name}", branches: %w{master dev})
143
+ end
144
+
145
+ template_job "my-job", "This is my job" do |j|
146
+ j.builders << shell("rake deploy")
147
+ end
148
+ ```
149
+
150
+ ### Things to Note
151
+
152
+ * **Jujube** doesn't do any input validation. Rather than duplicating the validation that
153
+ jenkins-job-builder already does, **Jujube** accepts any component options provided in a job
154
+ definition and passes them through to the YAML file. This actually makes **Jujube** quite
155
+ flexible and able to automatically support new options that are added in jenkins-job-builder.
156
+
157
+ * **Jujube** does not yet support all of the components that jenkins-job-builder supports. New
158
+ components are easy to add, and pull requests are more than welcome.
159
+
160
+ ## Extending Jujube
161
+
162
+ The `Job` class is the main class in **Jujube**. It should be essentially complete now. The
163
+ main work left to be done is to define the remaining components supported by jenkins-job-builder.
164
+ Components are defined in the various files in the `components` sub-directory. There is one file
165
+ per component type.
166
+
167
+ Most components can be defined using the `standard_component` "macro". If the component simply
168
+ takes a hash of options, then it is a standard component. Otherwise, a specialized method must
169
+ be written for the component. There are a few examples of these in the codebase.
170
+
171
+ If you'd like to add a component that requires some odd configuration and you're not sure how to
172
+ tackle it, contact me and I'll be happy to help.
173
+
174
+ ## Contributing
175
+
176
+ 1. Fork it
177
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
178
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
179
+ 4. Push to the branch (`git push origin my-new-feature`)
180
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+ require "yard"
4
+
5
+ task :default => [:test, :acceptance]
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.pattern = "test/**/*_test.rb"
9
+ end
10
+
11
+ Rake::TestTask.new(:acceptance) do |t|
12
+ t.pattern = "acceptance/**/*_test.rb"
13
+ end
14
+
15
+ YARD::Rake::YardocTask.new
@@ -0,0 +1,43 @@
1
+ job "endToEnd" do |j|
2
+ j.description = "DESCRIPTION"
3
+ j.node = "NODE"
4
+ j.block_upstream = true
5
+ j.block_downstream = false
6
+ j.quiet_period = 42
7
+
8
+ j.axes << slave(:arch, %w{i386 amd64})
9
+
10
+ j.scm << git(url: "URL", branches: %w{master dev}, wipe_workspace: false)
11
+
12
+ j.triggers << pollscm("INTERVAL")
13
+ j.triggers << pollurl(cron: "CRON", polling_node: "NODE") do |urls|
14
+ urls << url("URL",
15
+ proxy: false,
16
+ timeout: 442,
17
+ username: "USERNAME",
18
+ password: "PASSWORD",
19
+ check_status: 202,
20
+ check_etag: false,
21
+ check_date: true) do |content|
22
+ content << simple
23
+ content << json("JSON_PATH1", "JSON_PATH2")
24
+ content << xml("XPATH1", "XPATH2")
25
+ content << text("REGEX1", "REGEX2")
26
+ end
27
+ end
28
+
29
+ j.wrappers << timeout(type: 'elastic', elastic_percentage: 150, elastic_default_timeout: 5, fail: true)
30
+ j.wrappers << timestamps
31
+
32
+ j.builders << shell("COMMAND")
33
+
34
+ j.publishers << archive(artifacts: "ARTIFACTS", latest_only: true, allow_empty: true)
35
+ j.publishers << cppcheck(pattern: "PATTERN")
36
+ j.publishers << email_ext(recipients: %w{fred barney})
37
+ j.publishers << ircbot(notify_start: true)
38
+ j.publishers << junit(results: "RESULTS", keep_long_stdio: false)
39
+ j.publishers << trigger(project: "PROJECT")
40
+ j.publishers << xunit do |types|
41
+ types << unittest(pattern: "PATTERN", deleteoutput: false)
42
+ end
43
+ end
@@ -0,0 +1,80 @@
1
+ ---
2
+ - job:
3
+ name: endToEnd
4
+ description: DESCRIPTION
5
+ node: NODE
6
+ block-upstream: true
7
+ block-downstream: false
8
+ quiet-period: 42
9
+ project-type: matrix
10
+ axes:
11
+ - axis:
12
+ type: slave
13
+ name: arch
14
+ values:
15
+ - i386
16
+ - amd64
17
+ scm:
18
+ - git:
19
+ url: URL
20
+ branches:
21
+ - master
22
+ - dev
23
+ wipe-workspace: false
24
+ triggers:
25
+ - pollscm: INTERVAL
26
+ - pollurl:
27
+ cron: CRON
28
+ polling-node: NODE
29
+ urls:
30
+ - url: URL
31
+ proxy: false
32
+ timeout: 442
33
+ username: USERNAME
34
+ password: PASSWORD
35
+ check-status: 202
36
+ check-etag: false
37
+ check-date: true
38
+ check-content:
39
+ - simple: true
40
+ - json:
41
+ - JSON_PATH1
42
+ - JSON_PATH2
43
+ - xml:
44
+ - XPATH1
45
+ - XPATH2
46
+ - text:
47
+ - REGEX1
48
+ - REGEX2
49
+ wrappers:
50
+ - timeout:
51
+ type: elastic
52
+ elastic-percentage: 150
53
+ elastic-default-timeout: 5
54
+ fail: true
55
+ - timestamps
56
+ builders:
57
+ - shell: COMMAND
58
+ publishers:
59
+ - archive:
60
+ artifacts: ARTIFACTS
61
+ latest-only: true
62
+ allow-empty: true
63
+ - cppcheck:
64
+ pattern: PATTERN
65
+ - email-ext:
66
+ recipients:
67
+ - fred
68
+ - barney
69
+ - ircbot:
70
+ notify-start: true
71
+ - junit:
72
+ results: RESULTS
73
+ keep-long-stdio: false
74
+ - trigger:
75
+ project: PROJECT
76
+ - xunit:
77
+ types:
78
+ - unittest:
79
+ pattern: PATTERN
80
+ deleteoutput: false
@@ -0,0 +1 @@
1
+ job "job1"
@@ -0,0 +1 @@
1
+ job "job2"
@@ -0,0 +1,2 @@
1
+ job "job1"
2
+ job "job2"
@@ -0,0 +1 @@
1
+ job "job1"
@@ -0,0 +1 @@
1
+ job "job2"
@@ -0,0 +1,3 @@
1
+ job "example" do |j|
2
+ j.description = "DESCRIPTION"
3
+ end
@@ -0,0 +1,47 @@
1
+ require "minitest/autorun"
2
+ require "open3"
3
+ require "pathname"
4
+ require "tmpdir"
5
+
6
+ class AcceptanceTest < Minitest::Test
7
+ def with_fixture(fixture_name)
8
+ fixture = fixture_root.join(fixture_name)
9
+ Dir.mktmpdir do |temp_directory|
10
+ temp_directory = Pathname.new(temp_directory)
11
+ FileUtils.cp_r(fixture, temp_directory, preserve: true)
12
+ working = temp_directory.join(fixture_name)
13
+ Dir.chdir(working) do
14
+ yield
15
+ end
16
+ end
17
+ end
18
+
19
+ def run_jujube(*arguments)
20
+ jujube = bin_directory.join("jujube").to_s
21
+ @output, @error_output, @status = Open3.capture3(jujube, *arguments)
22
+ end
23
+
24
+ def assert_exits_cleanly
25
+ assert_equal(0, @status.exitstatus, @error_output)
26
+ end
27
+
28
+ def assert_file_exists(filename)
29
+ assert(File.exists?(filename), "File #{filename} does not exist")
30
+ end
31
+
32
+ def assert_directory_exists(filename)
33
+ assert(Dir.exists?(filename), "Directory #{filename} does not exist")
34
+ end
35
+
36
+ def fixture_root
37
+ acceptance_root.join("fixtures")
38
+ end
39
+
40
+ def bin_directory
41
+ acceptance_root.join("..", "bin")
42
+ end
43
+
44
+ def acceptance_root
45
+ Pathname.new(__FILE__).dirname.join("..").expand_path
46
+ end
47
+ end