magi 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +12 -3
- data/Procfile +4 -0
- data/README.md +25 -1
- data/app/assets/stylesheets/application.css +1 -0
- data/app/assets/stylesheets/builds.scss +35 -0
- data/app/assets/stylesheets/common.scss +69 -0
- data/app/assets/stylesheets/jobs.scss +121 -0
- data/app/assets/stylesheets/layout.scss +19 -0
- data/app/assets/stylesheets/mixin.scss +6 -0
- data/app/controllers/application_controller.rb +16 -0
- data/app/controllers/builds_controller.rb +45 -0
- data/app/controllers/jobs_controller.rb +43 -0
- data/app/models/build.rb +39 -0
- data/app/models/job.rb +88 -0
- data/app/views/builds/show.html.slim +36 -0
- data/app/views/jobs/_form.html.slim +27 -0
- data/app/views/jobs/edit.html.slim +9 -0
- data/app/views/jobs/index.html.slim +19 -0
- data/app/views/jobs/new.html.slim +7 -0
- data/app/views/jobs/show.html.slim +24 -0
- data/app/views/layouts/application.html.slim +10 -0
- data/app/workers/build_worker.rb +8 -0
- data/bin/magi +6 -0
- data/config/application.rb +1 -1
- data/config/database.yml +21 -17
- data/config/routes.rb +4 -55
- data/db/migrate/20130531143239_create_jobs.rb +10 -0
- data/db/migrate/20130531155155_create_builds.rb +14 -0
- data/db/schema.rb +35 -0
- data/lib/magi/command.rb +53 -0
- data/lib/magi/executer.rb +31 -0
- data/lib/magi/scheduler.rb +55 -0
- data/lib/magi/version.rb +1 -1
- data/lib/magi.rb +1 -4
- data/magi.gemspec +29 -1
- data/script/clock.rb +5 -0
- data/spec/factories/build.rb +6 -0
- data/spec/factories/job.rb +7 -0
- data/spec/models/build_spec.rb +35 -0
- data/spec/models/job_spec.rb +54 -0
- data/spec/requests/builds_spec.rb +68 -0
- data/spec/requests/jobs_spec.rb +93 -0
- data/spec/spec_helper.rb +28 -0
- metadata +324 -50
- data/.gitignore +0 -19
- data/app/mailers/.gitkeep +0 -0
- data/app/models/.gitkeep +0 -0
- data/app/views/layouts/application.html.erb +0 -14
- data/doc/README_FOR_APP +0 -2
- data/lib/assets/.gitkeep +0 -0
- data/lib/tasks/.gitkeep +0 -0
- data/log/.gitkeep +0 -0
- data/public/index.html +0 -241
- data/test/fixtures/.gitkeep +0 -0
- data/test/functional/.gitkeep +0 -0
- data/test/integration/.gitkeep +0 -0
- data/test/performance/browsing_test.rb +0 -12
- data/test/test_helper.rb +0 -13
- data/test/unit/.gitkeep +0 -0
- data/vendor/assets/javascripts/.gitkeep +0 -0
- data/vendor/assets/stylesheets/.gitkeep +0 -0
- data/vendor/plugins/.gitkeep +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 912ef942160db8fe16a33b901a9213a40dc8482e
|
4
|
+
data.tar.gz: 5a3f6069f1c918788815dde909a74469a3a3e047
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1b768a1e1a60ffcd0dcf7b8c7f78ffa004e5ffc4c64b395bc852a18ae964f638fe3520dd4331604dd80fda472de21b5c80ddf4b70506c1c88106c03f49a118a0
|
7
|
+
data.tar.gz: cedf3fd66a1b876a3f64a34dee6effe1ab0659b29603888508ab8a5ae2c0681ec2e4352c677fd14a1c54af95f3985f1b765f104e96147d39eba3c08355a0532a
|
data/Gemfile
CHANGED
@@ -1,19 +1,28 @@
|
|
1
1
|
source "https://rubygems.org"
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
gemspec
|
4
|
+
|
5
|
+
gem "font-awesome-rails"
|
5
6
|
gem "jquery-rails"
|
7
|
+
gem "quiet_assets"
|
8
|
+
gem "sidekiq"
|
9
|
+
gem "slim"
|
6
10
|
|
7
11
|
group :development, :test do
|
8
12
|
gem "pry-rails"
|
9
13
|
end
|
10
14
|
|
11
15
|
group :test do
|
16
|
+
gem "factory_girl_rails"
|
17
|
+
gem "response_code_matchers"
|
18
|
+
gem "rspec-json_matcher"
|
12
19
|
gem "rspec-rails"
|
20
|
+
gem "simplecov"
|
21
|
+
gem "thin"
|
13
22
|
end
|
14
23
|
|
15
24
|
group :assets do
|
16
|
-
gem "sass-rails",
|
25
|
+
gem "sass-rails", "~> 3.2.3"
|
17
26
|
gem "coffee-rails", "~> 3.2.1"
|
18
27
|
gem "uglifier", ">= 1.0.3"
|
19
28
|
end
|
data/Procfile
ADDED
data/README.md
CHANGED
@@ -1,6 +1,30 @@
|
|
1
1
|
# Magi
|
2
|
+
A continuous integration server implementation.
|
2
3
|
|
3
|
-
|
4
|
+
![](http://dl.dropbox.com/u/5978869/image/20130607_012059.png)
|
5
|
+
|
6
|
+
## Install
|
4
7
|
```
|
5
8
|
$ gem install magi
|
9
|
+
$ brew install mysql redis # or other commands to install MySQL and Redis
|
10
|
+
```
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
```
|
14
|
+
$ magi setup
|
15
|
+
$ magi start # open http://localhost:3000
|
6
16
|
```
|
17
|
+
|
18
|
+
## Hack
|
19
|
+
Magi is just a rails application.
|
20
|
+
Feel free to do what you want.
|
21
|
+
|
22
|
+
### tools
|
23
|
+
* Rails: HTTP server
|
24
|
+
* Sidekiq: background worker
|
25
|
+
* Redis: worker queue
|
26
|
+
* MySQL: store jobs & builds
|
27
|
+
|
28
|
+
### todo
|
29
|
+
* plugin system
|
30
|
+
* cli options (e.g. http port, rails env, redis conf)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
@import "mixin";
|
2
|
+
|
3
|
+
section.build {
|
4
|
+
&.success h1 {
|
5
|
+
color: #27AE60;
|
6
|
+
}
|
7
|
+
|
8
|
+
&.failure h1 {
|
9
|
+
color: #C0392B;
|
10
|
+
}
|
11
|
+
|
12
|
+
&.success h1 span:before {
|
13
|
+
content: "\F058";
|
14
|
+
}
|
15
|
+
|
16
|
+
&.build.failure h1 span:before {
|
17
|
+
content: "\F057";
|
18
|
+
}
|
19
|
+
|
20
|
+
&.build.unfinished h1 span:before {
|
21
|
+
content: "\F056";
|
22
|
+
}
|
23
|
+
|
24
|
+
td {
|
25
|
+
padding-right: 1em;
|
26
|
+
}
|
27
|
+
|
28
|
+
pre {
|
29
|
+
@include box;
|
30
|
+
margin-top: 1em;
|
31
|
+
padding: .5em 1em;
|
32
|
+
background: #eee;
|
33
|
+
overflow-x: scroll;
|
34
|
+
}
|
35
|
+
}
|
@@ -0,0 +1,69 @@
|
|
1
|
+
@import "mixin";
|
2
|
+
|
3
|
+
* {
|
4
|
+
margin: 0;
|
5
|
+
padding: 0;
|
6
|
+
}
|
7
|
+
|
8
|
+
body {
|
9
|
+
color: #333;
|
10
|
+
font-family: "Helvetica Neue", "Calibri", sans-serif;
|
11
|
+
line-height: 1.7;
|
12
|
+
}
|
13
|
+
|
14
|
+
a {
|
15
|
+
color: inherit;
|
16
|
+
text-decoration: none;
|
17
|
+
}
|
18
|
+
|
19
|
+
i {
|
20
|
+
margin-right: .3em;
|
21
|
+
}
|
22
|
+
|
23
|
+
textarea,
|
24
|
+
input {
|
25
|
+
font-size: 1em;
|
26
|
+
}
|
27
|
+
|
28
|
+
textarea,
|
29
|
+
input[type="text"] {
|
30
|
+
@include box;
|
31
|
+
width: 100%;
|
32
|
+
}
|
33
|
+
|
34
|
+
*:before {
|
35
|
+
margin-right: .3em;
|
36
|
+
font-family: FontAwesome;
|
37
|
+
}
|
38
|
+
|
39
|
+
.button {
|
40
|
+
@include box;
|
41
|
+
width: 10em;
|
42
|
+
font-size: 1em;
|
43
|
+
color: white;
|
44
|
+
background: #F39C12;
|
45
|
+
border-color: #F39C12;
|
46
|
+
line-height: inherit;
|
47
|
+
cursor: pointer;
|
48
|
+
text-align: center;
|
49
|
+
}
|
50
|
+
|
51
|
+
section.breadcrumbs {
|
52
|
+
ul {
|
53
|
+
@include box;
|
54
|
+
border-color: #eee;
|
55
|
+
background: #eee;
|
56
|
+
list-style: none;
|
57
|
+
overflow: hidden;
|
58
|
+
color: #888;
|
59
|
+
}
|
60
|
+
|
61
|
+
li {
|
62
|
+
float: left;
|
63
|
+
margin-right: 1em;
|
64
|
+
}
|
65
|
+
|
66
|
+
li.delimiter:before {
|
67
|
+
content: "\F105";
|
68
|
+
}
|
69
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
@import "mixin";
|
2
|
+
|
3
|
+
section.jobs {
|
4
|
+
h1:before {
|
5
|
+
content: "\F13D";
|
6
|
+
}
|
7
|
+
|
8
|
+
p {
|
9
|
+
color: #ccc;
|
10
|
+
}
|
11
|
+
|
12
|
+
ul {
|
13
|
+
margin-top: 1em;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
section.job_settings {
|
18
|
+
h1:before {
|
19
|
+
content: "\F085";
|
20
|
+
}
|
21
|
+
|
22
|
+
td {
|
23
|
+
padding-right: 1em;
|
24
|
+
}
|
25
|
+
|
26
|
+
input[type="submit"] {
|
27
|
+
margin-top: 1em;
|
28
|
+
background: #34495e;
|
29
|
+
border-color: #34495e;
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
section.jobs,
|
34
|
+
section.builds {
|
35
|
+
.buttons {
|
36
|
+
overflow: hidden;
|
37
|
+
}
|
38
|
+
|
39
|
+
.buttons a {
|
40
|
+
float: left;
|
41
|
+
display: block;
|
42
|
+
margin-right: 1em;
|
43
|
+
}
|
44
|
+
|
45
|
+
.buttons a.new:before {
|
46
|
+
content: "\F040";
|
47
|
+
}
|
48
|
+
|
49
|
+
.buttons a.run:before {
|
50
|
+
content: "\F04B";
|
51
|
+
}
|
52
|
+
|
53
|
+
.buttons a.new,
|
54
|
+
.buttons a.settings {
|
55
|
+
background: #2C3E50;
|
56
|
+
border-color: #2C3E50;
|
57
|
+
}
|
58
|
+
|
59
|
+
.buttons a.settings:before {
|
60
|
+
content: "\F013";
|
61
|
+
}
|
62
|
+
|
63
|
+
.buttons a.delete {
|
64
|
+
background: #C0392B;
|
65
|
+
border-color: #C0392B;
|
66
|
+
}
|
67
|
+
|
68
|
+
.buttons a.delete:before {
|
69
|
+
content: "\F014";
|
70
|
+
}
|
71
|
+
|
72
|
+
.button {
|
73
|
+
margin-top: .5em;
|
74
|
+
}
|
75
|
+
|
76
|
+
ul {
|
77
|
+
margin-top: 1em;
|
78
|
+
list-style: none;
|
79
|
+
}
|
80
|
+
|
81
|
+
li a {
|
82
|
+
@include box;
|
83
|
+
display: block;
|
84
|
+
}
|
85
|
+
|
86
|
+
li a:hover {
|
87
|
+
background: #fafafa;
|
88
|
+
}
|
89
|
+
|
90
|
+
li.unfinished a:before {
|
91
|
+
content: "\F056";
|
92
|
+
}
|
93
|
+
|
94
|
+
li.success a:before {
|
95
|
+
content: "\F058";
|
96
|
+
color: #27AE60;
|
97
|
+
}
|
98
|
+
|
99
|
+
li.failure a:before {
|
100
|
+
content: "\F057";
|
101
|
+
color: #C0392B;
|
102
|
+
}
|
103
|
+
|
104
|
+
li.success a {
|
105
|
+
border-color: #27AE60;
|
106
|
+
}
|
107
|
+
|
108
|
+
li.failure a {
|
109
|
+
border-color: #C0392B;
|
110
|
+
}
|
111
|
+
|
112
|
+
li span {
|
113
|
+
margin-right: 1em;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
section.builds {
|
118
|
+
h1:before {
|
119
|
+
content: "\F0FB";
|
120
|
+
}
|
121
|
+
}
|
@@ -1,3 +1,19 @@
|
|
1
1
|
class ApplicationController < ActionController::Base
|
2
2
|
protect_from_forgery
|
3
|
+
|
4
|
+
respond_to :html, :json
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def require_resource_params
|
9
|
+
head 400 unless resource_params
|
10
|
+
end
|
11
|
+
|
12
|
+
def resource_name
|
13
|
+
self.class.name.split("::").last.sub(/Controller$/, "").singularize.underscore
|
14
|
+
end
|
15
|
+
|
16
|
+
def resource_params
|
17
|
+
params[resource_name]
|
18
|
+
end
|
3
19
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
class BuildsController < ApplicationController
|
2
|
+
before_filter :require_job
|
3
|
+
before_filter :require_resources, only: :index
|
4
|
+
before_filter :require_resource, only: [:show, :update, :destroy]
|
5
|
+
before_filter :require_resource_params, only: :update
|
6
|
+
|
7
|
+
def index
|
8
|
+
respond_with @resources
|
9
|
+
end
|
10
|
+
|
11
|
+
def show
|
12
|
+
respond_with @resource
|
13
|
+
end
|
14
|
+
|
15
|
+
def create
|
16
|
+
resource = @job.queue
|
17
|
+
respond_with resource, location: [@job, resource]
|
18
|
+
end
|
19
|
+
|
20
|
+
def update
|
21
|
+
respond_with @resource.update_attributes(resource_params.slice(:status))
|
22
|
+
end
|
23
|
+
|
24
|
+
def destroy
|
25
|
+
respond_with @resource.destroy
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def require_job
|
31
|
+
@job = Job.find(params[:job_id])
|
32
|
+
end
|
33
|
+
|
34
|
+
def scope
|
35
|
+
@job.builds
|
36
|
+
end
|
37
|
+
|
38
|
+
def require_resources
|
39
|
+
@resources = scope
|
40
|
+
end
|
41
|
+
|
42
|
+
def require_resource
|
43
|
+
@resource = scope.find(params[:id])
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class JobsController < ApplicationController
|
2
|
+
before_filter :require_resource_params, only: [:create, :update]
|
3
|
+
before_filter :require_resources, only: :index
|
4
|
+
before_filter :require_resource, only: [:show, :edit, :update, :destroy]
|
5
|
+
|
6
|
+
def index
|
7
|
+
respond_with @resources
|
8
|
+
end
|
9
|
+
|
10
|
+
def show
|
11
|
+
respond_with @resource
|
12
|
+
end
|
13
|
+
|
14
|
+
def new
|
15
|
+
respond_with @resource = scope.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def create
|
19
|
+
respond_with @resource = scope.create_with_properties(resource_params)
|
20
|
+
end
|
21
|
+
|
22
|
+
def update
|
23
|
+
respond_with @resource.update_attributes_with_properties(resource_params)
|
24
|
+
end
|
25
|
+
|
26
|
+
def destroy
|
27
|
+
respond_with @resource.destroy
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def scope
|
33
|
+
Job.scoped
|
34
|
+
end
|
35
|
+
|
36
|
+
def require_resources
|
37
|
+
@resources = scope
|
38
|
+
end
|
39
|
+
|
40
|
+
def require_resource
|
41
|
+
@resource = scope.find(params[:id])
|
42
|
+
end
|
43
|
+
end
|
data/app/models/build.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
class Build < ActiveRecord::Base
|
2
|
+
attr_accessible :finished_at, :started_at, :output, :status
|
3
|
+
|
4
|
+
validates :job_id, presence: true
|
5
|
+
|
6
|
+
belongs_to :job, touch: true
|
7
|
+
|
8
|
+
scope :recent, -> { order("created_at DESC") }
|
9
|
+
|
10
|
+
scope :finished, -> { where("finished_at IS NOT NULL") }
|
11
|
+
|
12
|
+
# Pushes this build to worker's queue.
|
13
|
+
def queue
|
14
|
+
BuildWorker.perform_async(id)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Runs this build, usually called from BuildWorker#perform.
|
18
|
+
def start
|
19
|
+
update_attributes!(started_at: Time.now)
|
20
|
+
result = job.start
|
21
|
+
update_attributes!(finished_at: Time.now, status: result[:status], output: result[:output])
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns elapsed sec as a Float or nil.
|
25
|
+
def sec
|
26
|
+
finished_at - started_at if finished_at && started_at
|
27
|
+
end
|
28
|
+
|
29
|
+
def status_name
|
30
|
+
case status
|
31
|
+
when true
|
32
|
+
"success"
|
33
|
+
when false
|
34
|
+
"failure"
|
35
|
+
when nil
|
36
|
+
"unfinished"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/app/models/job.rb
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
class Job < ActiveRecord::Base
|
2
|
+
attr_accessible :name, :config
|
3
|
+
|
4
|
+
serialize :config, Hash
|
5
|
+
|
6
|
+
validates :name, presence: true
|
7
|
+
|
8
|
+
has_many :builds
|
9
|
+
|
10
|
+
scope :recent, -> { order("updated_at DESC") }
|
11
|
+
|
12
|
+
delegate :status, :status_name, :status_icon_css_class, to: :last_finished_build, allow_nil: true
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def create_with_properties(properties)
|
16
|
+
job = new
|
17
|
+
properties.each {|key, value| job.send("#{key}=", value) }
|
18
|
+
job.tap(&:save)
|
19
|
+
end
|
20
|
+
|
21
|
+
def queue
|
22
|
+
select(&:scheduled?).each(&:queue)
|
23
|
+
end
|
24
|
+
|
25
|
+
def property(name)
|
26
|
+
properties << name
|
27
|
+
|
28
|
+
define_method(name) do
|
29
|
+
config[name.to_s]
|
30
|
+
end
|
31
|
+
|
32
|
+
define_method("#{name}=") do |value|
|
33
|
+
config[name.to_s] = value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def properties
|
38
|
+
@properties ||= []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
property(:description)
|
43
|
+
|
44
|
+
property(:schedule)
|
45
|
+
|
46
|
+
property(:script)
|
47
|
+
|
48
|
+
def start
|
49
|
+
script ? execute : raise_script_not_found
|
50
|
+
end
|
51
|
+
|
52
|
+
def scheduler
|
53
|
+
Magi::Scheduler.new(schedule)
|
54
|
+
end
|
55
|
+
|
56
|
+
def scheduled?
|
57
|
+
schedule && scheduler.scheduled?
|
58
|
+
end
|
59
|
+
|
60
|
+
def queue
|
61
|
+
builds.create.tap(&:queue)
|
62
|
+
end
|
63
|
+
|
64
|
+
def status_name
|
65
|
+
last_finished_build.try(:status_name) || "unfinished"
|
66
|
+
end
|
67
|
+
|
68
|
+
def last_finished_build
|
69
|
+
builds.finished.order(:finished_at).last
|
70
|
+
end
|
71
|
+
|
72
|
+
def update_attributes_with_properties(properties)
|
73
|
+
properties.each {|key, value| send("#{key}=", value) }
|
74
|
+
tap(&:save)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def execute
|
80
|
+
Magi::Executer.execute(script)
|
81
|
+
end
|
82
|
+
|
83
|
+
def raise_script_not_found
|
84
|
+
raise ScriptNotFound, 'You must set script["config"]'
|
85
|
+
end
|
86
|
+
|
87
|
+
class ScriptNotFound < StandardError; end
|
88
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
section.breadcrumbs
|
2
|
+
ul
|
3
|
+
li= link_to "Home", jobs_path
|
4
|
+
li.delimiter
|
5
|
+
li= link_to @resource.job.name, @resource.job
|
6
|
+
li.delimiter
|
7
|
+
li= link_to "##{@resource.id}", [@resource.job, @resource]
|
8
|
+
|
9
|
+
section.build class = @resource.status_name
|
10
|
+
h1
|
11
|
+
span ##{@resource.id}
|
12
|
+
|
13
|
+
table
|
14
|
+
tr
|
15
|
+
td started_at
|
16
|
+
td= ":"
|
17
|
+
td= @resource.started_at
|
18
|
+
|
19
|
+
tr
|
20
|
+
td finished_at
|
21
|
+
td= ":"
|
22
|
+
td= @resource.finished_at
|
23
|
+
|
24
|
+
tr
|
25
|
+
td sec
|
26
|
+
td= ":"
|
27
|
+
td= @resource.sec
|
28
|
+
|
29
|
+
tr
|
30
|
+
td status
|
31
|
+
td= ":"
|
32
|
+
td= @resource.status_name
|
33
|
+
|
34
|
+
pre
|
35
|
+
code
|
36
|
+
= @resource.output
|
@@ -0,0 +1,27 @@
|
|
1
|
+
section.job_settings
|
2
|
+
h1 Settings
|
3
|
+
|
4
|
+
= form_for resource do |f|
|
5
|
+
.field
|
6
|
+
p= f.label :name
|
7
|
+
p= f.text_field :name, placeholder: "required"
|
8
|
+
|
9
|
+
.field
|
10
|
+
p= f.label :description
|
11
|
+
p= f.text_area :description, rows: 3
|
12
|
+
|
13
|
+
.field
|
14
|
+
p= f.label :script
|
15
|
+
p= f.text_area :script, rows: 6
|
16
|
+
|
17
|
+
.field
|
18
|
+
p= f.label :schedule
|
19
|
+
p= f.text_field :schedule, placeholder: "* * * * *"
|
20
|
+
|
21
|
+
- (resource.class.properties - [:description, :schedule, :script]).each do |name|
|
22
|
+
.field
|
23
|
+
p= f.label name
|
24
|
+
p= f.text_field name
|
25
|
+
|
26
|
+
.field
|
27
|
+
p= f.submit class: "button"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
section.breadcrumbs
|
2
|
+
ul
|
3
|
+
li= link_to "Home", jobs_path
|
4
|
+
|
5
|
+
section.jobs
|
6
|
+
h1 JOBS
|
7
|
+
|
8
|
+
p #{Job.count} jobs are found.
|
9
|
+
|
10
|
+
|
11
|
+
.buttons
|
12
|
+
= link_to "New", [:new, :job], class: "button new"
|
13
|
+
|
14
|
+
ul
|
15
|
+
- @resources.recent.each do |resource|
|
16
|
+
li class = resource.status_name
|
17
|
+
= link_to resource do
|
18
|
+
span
|
19
|
+
= resource.name
|
@@ -0,0 +1,24 @@
|
|
1
|
+
section.breadcrumbs
|
2
|
+
ul
|
3
|
+
li= link_to "Home", jobs_path
|
4
|
+
li.delimiter
|
5
|
+
li= link_to @resource.name, @resource
|
6
|
+
|
7
|
+
section.builds
|
8
|
+
h1
|
9
|
+
= @resource.name
|
10
|
+
|
11
|
+
p
|
12
|
+
= @resource.description || "(no description)"
|
13
|
+
|
14
|
+
.buttons
|
15
|
+
= link_to "Run", [@resource, :builds], method: "POST", class: "button run"
|
16
|
+
= link_to "Settings", [:edit, @resource], class: "button settings"
|
17
|
+
= link_to "Delete", @resource, method: "DELETE", confirm: "Are you sure?", class: "button delete"
|
18
|
+
|
19
|
+
ul
|
20
|
+
- @resource.builds.recent.limit(10).each do |build|
|
21
|
+
li class = build.status_name
|
22
|
+
= link_to [@resource, build] do
|
23
|
+
span.id ##{build.id}
|
24
|
+
span= build.finished_at
|