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