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
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
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
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 @@
|
|
1
|
+
job "job1"
|
@@ -0,0 +1 @@
|
|
1
|
+
job "job2"
|
@@ -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
|