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
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
|
+
[](https://travis-ci.org/randycoulman/jujube)
|
4
|
+
[](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
|