perkins 0.0.1
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/.DS_Store +0 -0
- data/.env.example +4 -0
- data/.gitignore +19 -0
- data/.pryrc +3 -0
- data/.rspec +2 -0
- data/Gemfile +18 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +28 -0
- data/TODO.md +4 -0
- data/bin/perkins +6 -0
- data/db/migrate/20150130143030_create_repo.rb +18 -0
- data/db/migrate/20150130143050_create_builds.rb +20 -0
- data/db/schema.rb +38 -0
- data/examples/config.rb +12 -0
- data/examples/database.yml +17 -0
- data/examples/mongo.yml +13 -0
- data/lib/core_ext/hash/compact.rb +8 -0
- data/lib/core_ext/hash/deep_merge.rb +15 -0
- data/lib/core_ext/hash/deep_symbolize_keys.rb +20 -0
- data/lib/core_ext/object/false.rb +5 -0
- data/lib/core_ext/string/indent.rb +5 -0
- data/lib/core_ext/string/unindent.rb +5 -0
- data/lib/perkins/.DS_Store +0 -0
- data/lib/perkins/application.rb +40 -0
- data/lib/perkins/assets/images/github.svg +4 -0
- data/lib/perkins/assets/images/spinner.svg +23 -0
- data/lib/perkins/assets/javascripts/app.js +9 -0
- data/lib/perkins/assets/javascripts/log_view.js.coffee +95 -0
- data/lib/perkins/assets/javascripts/perkings.js.coffee +40 -0
- data/lib/perkins/assets/javascripts/vendor/ansiparse.js +187 -0
- data/lib/perkins/assets/javascripts/vendor/jquery.timeago.js +189 -0
- data/lib/perkins/assets/javascripts/vendor/log.js +2 -0
- data/lib/perkins/assets/javascripts/vendor/minispade.js +55 -0
- data/lib/perkins/assets/stylesheets/app.css +2 -0
- data/lib/perkins/assets/stylesheets/log.css.scss +115 -0
- data/lib/perkins/assets/stylesheets/styles.css.scss +199 -0
- data/lib/perkins/auth/github.rb +181 -0
- data/lib/perkins/build/data/env.rb +84 -0
- data/lib/perkins/build/data/var.rb +60 -0
- data/lib/perkins/build/data.rb +167 -0
- data/lib/perkins/build/script/bundler.rb +76 -0
- data/lib/perkins/build/script/go.rb +100 -0
- data/lib/perkins/build/script/helpers.rb +39 -0
- data/lib/perkins/build/script/jdk.rb +43 -0
- data/lib/perkins/build/script/ruby.rb +31 -0
- data/lib/perkins/build/script/rvm.rb +73 -0
- data/lib/perkins/build/script/stages.rb +28 -0
- data/lib/perkins/build/script/templates/footer.sh +3 -0
- data/lib/perkins/build/script/templates/header.sh +201 -0
- data/lib/perkins/build/script/templates/xcode.sh +21 -0
- data/lib/perkins/build/script.rb +167 -0
- data/lib/perkins/build/shell/dsl.rb +104 -0
- data/lib/perkins/build/shell/node.rb +121 -0
- data/lib/perkins/build/shell.rb +16 -0
- data/lib/perkins/build.rb +27 -0
- data/lib/perkins/build_report.rb +11 -0
- data/lib/perkins/cli.rb +42 -0
- data/lib/perkins/commit.rb +30 -0
- data/lib/perkins/dsl/app_proxy.rb +23 -0
- data/lib/perkins/dsl.rb +12 -0
- data/lib/perkins/listener.rb +38 -0
- data/lib/perkins/logger.rb +12 -0
- data/lib/perkins/notifier.rb +5 -0
- data/lib/perkins/repo.rb +145 -0
- data/lib/perkins/runner.rb +124 -0
- data/lib/perkins/server.rb +314 -0
- data/lib/perkins/thor_utils.rb +79 -0
- data/lib/perkins/version.rb +3 -0
- data/lib/perkins/views/401.haml +1 -0
- data/lib/perkins/views/builds.haml +46 -0
- data/lib/perkins/views/index.haml +6 -0
- data/lib/perkins/views/layout.haml +53 -0
- data/lib/perkins/views/menu.haml +18 -0
- data/lib/perkins/views/orgs.haml +101 -0
- data/lib/perkins/views/profile.haml +31 -0
- data/lib/perkins/views/readme.md +20 -0
- data/lib/perkins/views/repos/config.haml +72 -0
- data/lib/perkins/views/repos/github.haml +76 -0
- data/lib/perkins/views/repos/menu.haml +17 -0
- data/lib/perkins/views/repos/repo.haml +64 -0
- data/lib/perkins/views/repos/spinner.haml +3 -0
- data/lib/perkins/webhooks/github.rb +12 -0
- data/lib/perkins/worker.rb +33 -0
- data/lib/perkins.rb +36 -0
- data/perkins.gemspec +52 -0
- data/spec/fixtures/.travis.yml +8 -0
- data/spec/fixtures/config.yml +6 -0
- data/spec/lib/build/build_spec.rb +58 -0
- data/spec/lib/commit_spec.rb +6 -0
- data/spec/lib/dsl_spec.rb +17 -0
- data/spec/lib/listener_spec.rb +30 -0
- data/spec/lib/repo_spec.rb +110 -0
- data/spec/lib/runner_spec.rb +76 -0
- data/spec/lib/server_spec.rb +108 -0
- data/spec/spec_helper.rb +67 -0
- data/spec/support/auth.rb +30 -0
- data/spec/support/github_api.rb +177 -0
- metadata +503 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
$serif: "freight-sans-pro",sans-serif !important;
|
2
|
+
$georgia: "ff-tisa-web-pro",Georgia,Cambria,"Times New Roman",Times,serif !important;
|
3
|
+
$background-color: #F9F9F6;
|
4
|
+
$soft-separator: #cecece;
|
5
|
+
$link-color: #5100a9;
|
6
|
+
$link-up: #222;
|
7
|
+
|
8
|
+
body {
|
9
|
+
padding-top: 50px;
|
10
|
+
position: relative;
|
11
|
+
background-color: $background-color;
|
12
|
+
font-size: 1.6em;
|
13
|
+
font-family: "ff-tisa-web-pro",Georgia,Cambria,"Times New Roman",Times,serif !important;;
|
14
|
+
a{
|
15
|
+
outline: none !important;
|
16
|
+
color:$link-color;
|
17
|
+
&:hover{
|
18
|
+
color: $link-up;
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
.container{
|
24
|
+
width: 100%;
|
25
|
+
}
|
26
|
+
|
27
|
+
nav{
|
28
|
+
background-color: #F9F9F6 !important;
|
29
|
+
}
|
30
|
+
|
31
|
+
h2, .h2, h4 {
|
32
|
+
font-family: "ff-tisa-web-pro",Georgia,Cambria,"Times New Roman",Times,serif !important;;
|
33
|
+
&.title{
|
34
|
+
font-family: $serif;
|
35
|
+
text-transform: uppercase;
|
36
|
+
color:#0e0e0e;
|
37
|
+
font-weight: bold;
|
38
|
+
}
|
39
|
+
&.centered{
|
40
|
+
margin: 43px 20px;
|
41
|
+
text-align: center;
|
42
|
+
color:#0e0e0e;
|
43
|
+
}
|
44
|
+
font-weight: 700;
|
45
|
+
}
|
46
|
+
|
47
|
+
|
48
|
+
nav.transparent.navbar {
|
49
|
+
//background: rgba(0,0,0,0.4);
|
50
|
+
background: transparent;
|
51
|
+
//border-bottom: 1px solid #1E1E1E;
|
52
|
+
a {
|
53
|
+
color: #222;
|
54
|
+
}
|
55
|
+
.navbar-inner a {
|
56
|
+
font-weight: lighter;
|
57
|
+
color: #222;
|
58
|
+
text-transform: uppercase;
|
59
|
+
font-size: 0.8em;
|
60
|
+
}
|
61
|
+
}
|
62
|
+
.navbar-toggle {
|
63
|
+
background-color: rgba(0, 0, 0, 0);
|
64
|
+
border: 1px solid #000;
|
65
|
+
&:focus{
|
66
|
+
background-color: #ddd;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
.navbar-toggle .icon-bar {
|
70
|
+
background-color: #000;
|
71
|
+
}
|
72
|
+
|
73
|
+
#footer {
|
74
|
+
border-top: 1px solid #ccc;
|
75
|
+
color: #888888;
|
76
|
+
margin-top: 0;
|
77
|
+
padding-bottom: 17px;
|
78
|
+
padding-top: 17px;
|
79
|
+
text-align: center;
|
80
|
+
//text-transform: uppercase;
|
81
|
+
}
|
82
|
+
|
83
|
+
.navbar {
|
84
|
+
background-color: #FFFFFF;
|
85
|
+
//border-bottom: 1px solid #ECECEC;
|
86
|
+
border-bottom: 1px solid #ccc;
|
87
|
+
font-weight: 500;
|
88
|
+
//position: relative;
|
89
|
+
transition: border-bottom-color 0s ease 0.1s;
|
90
|
+
}
|
91
|
+
|
92
|
+
.navbar-brand{
|
93
|
+
//color: #5100A9;
|
94
|
+
float: left;
|
95
|
+
font-family: $serif;
|
96
|
+
|
97
|
+
font-size: 37px;
|
98
|
+
font-style: inherit;
|
99
|
+
font-weight: 900;
|
100
|
+
margin-left: -15px;
|
101
|
+
margin-right: 5px;
|
102
|
+
text-transform: uppercase;
|
103
|
+
|
104
|
+
}
|
105
|
+
.navbar .nav,
|
106
|
+
.navbar .nav > li {
|
107
|
+
float:none;
|
108
|
+
display:inline-block;
|
109
|
+
*display:inline; /* ie7 fix */
|
110
|
+
*zoom:1; /* hasLayout ie7 trigger */
|
111
|
+
vertical-align: top;
|
112
|
+
a{
|
113
|
+
font-family:$serif;
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
.navbar-inner {
|
118
|
+
display: block;
|
119
|
+
//font-family: Runda-1;
|
120
|
+
//font-size: 1.5em;
|
121
|
+
line-height: 20px;
|
122
|
+
margin-left: auto;
|
123
|
+
margin-right: auto;
|
124
|
+
//max-width: 687px;
|
125
|
+
text-align: center;
|
126
|
+
}
|
127
|
+
|
128
|
+
.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus {
|
129
|
+
background-color: background;
|
130
|
+
border-radius: 0;
|
131
|
+
color: #fff;
|
132
|
+
}
|
133
|
+
|
134
|
+
.nav-pills > li > a, .nav-pills > li > a:hover, .nav-pills > li > a:focus {
|
135
|
+
background-color: #eee;
|
136
|
+
border-radius: 0;
|
137
|
+
color: #000;
|
138
|
+
}
|
139
|
+
|
140
|
+
|
141
|
+
.avatar{
|
142
|
+
border-radius: 50%;
|
143
|
+
}
|
144
|
+
|
145
|
+
|
146
|
+
#left-wrap{
|
147
|
+
min-width: 250px;
|
148
|
+
}
|
149
|
+
|
150
|
+
#sidebar{
|
151
|
+
border-right: 1px solid #cecece;
|
152
|
+
display: inline-block;
|
153
|
+
height: 100%;
|
154
|
+
left: 0px;
|
155
|
+
padding: 0;
|
156
|
+
width: 253px;
|
157
|
+
background-color: $background-color;
|
158
|
+
#sidebar-header{
|
159
|
+
border-bottom: 1px solid #cecece;
|
160
|
+
padding: 15px 15px 11px;
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
#main-content{
|
165
|
+
min-width: 600px;
|
166
|
+
}
|
167
|
+
|
168
|
+
#repo-menu{
|
169
|
+
//margin-top: 29px;
|
170
|
+
margin-top: 13px;
|
171
|
+
i{
|
172
|
+
padding: 3.7px;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
/*
|
177
|
+
pre#log{
|
178
|
+
background-color: #222;
|
179
|
+
border: 1px solid #ddd;
|
180
|
+
clear: left;
|
181
|
+
color: #f1f1f1;
|
182
|
+
counter-reset: line-numbering;
|
183
|
+
font-family: monospace;
|
184
|
+
font-size: 12px;
|
185
|
+
line-height: 19px;
|
186
|
+
margin-top: 1em;
|
187
|
+
min-height: 12px;
|
188
|
+
padding: 15px 23px;
|
189
|
+
position: relative;
|
190
|
+
white-space: pre-wrap;
|
191
|
+
word-wrap: break-word;
|
192
|
+
}*/
|
193
|
+
|
194
|
+
.build-row td{
|
195
|
+
font-size: 13px;
|
196
|
+
padding: 5px 20px 5px 0;
|
197
|
+
text-align: left;
|
198
|
+
vertical-align: top;
|
199
|
+
}
|
@@ -0,0 +1,181 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'warden/github'
|
3
|
+
|
4
|
+
#https://github.com/atmos/sinatra_auth_github
|
5
|
+
|
6
|
+
module Perkins
|
7
|
+
module Auth
|
8
|
+
module Github
|
9
|
+
# Simple way to serve an image early in the stack and not get blocked by
|
10
|
+
# application level before filters
|
11
|
+
class AccessDenied < Sinatra::Base
|
12
|
+
enable :raise_errors
|
13
|
+
disable :show_exceptions
|
14
|
+
|
15
|
+
get '/_images/securocat.png' do
|
16
|
+
send_file(File.join(File.dirname(__FILE__), "views", "securocat.png"))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# The default failure application, this is overridable from the extension config
|
21
|
+
class BadAuthentication < Sinatra::Base
|
22
|
+
enable :raise_errors
|
23
|
+
disable :show_exceptions
|
24
|
+
|
25
|
+
helpers do
|
26
|
+
def unauthorized_template
|
27
|
+
@unauthenticated_template ||= "401"
|
28
|
+
#File.read(File.join(File.dirname(__FILE__), "../../views", "401.html"))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
get '/unauthenticated' do
|
33
|
+
status 403
|
34
|
+
unauthorized_template
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module Helpers
|
39
|
+
def warden
|
40
|
+
env['warden']
|
41
|
+
end
|
42
|
+
|
43
|
+
def authenticate!(*args)
|
44
|
+
warden.authenticate!(*args)
|
45
|
+
end
|
46
|
+
|
47
|
+
def authenticated?(*args)
|
48
|
+
warden.authenticated?(*args)
|
49
|
+
end
|
50
|
+
|
51
|
+
def logout!
|
52
|
+
warden.logout
|
53
|
+
end
|
54
|
+
|
55
|
+
# The authenticated user object
|
56
|
+
#
|
57
|
+
# Supports a variety of methods, name, full_name, email, etc
|
58
|
+
def github_user
|
59
|
+
warden.user
|
60
|
+
end
|
61
|
+
|
62
|
+
# Send a V3 API GET request to path
|
63
|
+
#
|
64
|
+
# path - the path on api.github.com to hit
|
65
|
+
#
|
66
|
+
# Returns a rest client response object
|
67
|
+
#
|
68
|
+
# Examples
|
69
|
+
# github_raw_request("/user")
|
70
|
+
# # => RestClient::Response
|
71
|
+
def github_raw_request(path)
|
72
|
+
github_user.github_raw_request(path)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Send a V3 API GET request to path and parse the response body
|
76
|
+
#
|
77
|
+
# path - the path on api.github.com to hit
|
78
|
+
#
|
79
|
+
# Returns a parsed JSON response
|
80
|
+
#
|
81
|
+
# Examples
|
82
|
+
# github_request("/user")
|
83
|
+
# # => { 'login' => 'atmos', ... }
|
84
|
+
def github_request(path)
|
85
|
+
github_user.github_request(path)
|
86
|
+
end
|
87
|
+
|
88
|
+
# See if the user is a public member of the named organization
|
89
|
+
#
|
90
|
+
# name - the organization name
|
91
|
+
#
|
92
|
+
# Returns: true if the user is public access, false otherwise
|
93
|
+
def github_public_organization_access?(name)
|
94
|
+
github_user.publicized_organization_member?(name)
|
95
|
+
end
|
96
|
+
|
97
|
+
# See if the user is a member of the named organization
|
98
|
+
#
|
99
|
+
# name - the organization name
|
100
|
+
#
|
101
|
+
# Returns: true if the user has access, false otherwise
|
102
|
+
def github_organization_access?(name)
|
103
|
+
github_user.organization_member?(name)
|
104
|
+
end
|
105
|
+
|
106
|
+
# See if the user is a member of the team id
|
107
|
+
#
|
108
|
+
# team_id - the team's id
|
109
|
+
#
|
110
|
+
# Returns: true if the user has access, false otherwise
|
111
|
+
def github_team_access?(team_id)
|
112
|
+
github_user.team_member?(team_id)
|
113
|
+
end
|
114
|
+
|
115
|
+
# Enforce user membership to the named organization
|
116
|
+
#
|
117
|
+
# name - the organization to test membership against
|
118
|
+
#
|
119
|
+
# Returns an execution halt if the user is not a member of the named org
|
120
|
+
def github_public_organization_authenticate!(name)
|
121
|
+
authenticate!
|
122
|
+
halt([401, "Unauthorized User"]) unless github_public_organization_access?(name)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Enforce user membership to the named organization if membership is publicized
|
126
|
+
#
|
127
|
+
# name - the organization to test membership against
|
128
|
+
#
|
129
|
+
# Returns an execution halt if the user is not a member of the named org
|
130
|
+
def github_organization_authenticate!(name)
|
131
|
+
authenticate!
|
132
|
+
halt([401, "Unauthorized User"]) unless github_organization_access?(name)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Enforce user membership to the team id
|
136
|
+
#
|
137
|
+
# team_id - the team_id to test membership against
|
138
|
+
#
|
139
|
+
# Returns an execution halt if the user is not a member of the team
|
140
|
+
def github_team_authenticate!(team_id)
|
141
|
+
authenticate!
|
142
|
+
halt([401, "Unauthorized User"]) unless github_team_access?(team_id)
|
143
|
+
end
|
144
|
+
|
145
|
+
def _relative_url_for(path)
|
146
|
+
request.script_name + path
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.registered(app)
|
151
|
+
app.use AccessDenied
|
152
|
+
app.use BadAuthentication
|
153
|
+
|
154
|
+
app.use Warden::Manager do |manager|
|
155
|
+
manager.default_strategies :github
|
156
|
+
|
157
|
+
manager.failure_app = app.github_options[:failure_app] || BadAuthentication
|
158
|
+
|
159
|
+
manager.scope_defaults :default, :config => {
|
160
|
+
:client_id => app.github_options[:client_id] || ENV['GITHUB_CLIENT_ID'],
|
161
|
+
:client_secret => app.github_options[:secret] || ENV['GITHUB_CLIENT_SECRET'],
|
162
|
+
:scope => app.github_options[:scopes] || "admin:repo_hook,repo,user:email",
|
163
|
+
:redirect_uri => app.github_options[:callback_url] || '/auth/github/callback'
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
app.helpers Helpers
|
168
|
+
|
169
|
+
app.get '/auth/github/callback' do
|
170
|
+
if params["error"]
|
171
|
+
redirect "/unauthenticated"
|
172
|
+
else
|
173
|
+
authenticate!
|
174
|
+
return_to = session.delete('return_to') || _relative_url_for('/')
|
175
|
+
redirect return_to
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
module Perkins
|
5
|
+
module Build
|
6
|
+
class Data
|
7
|
+
class Env
|
8
|
+
delegate :secure_env_enabled?, :pull_request, :config, :build, :job, :repository, to: :data
|
9
|
+
|
10
|
+
class Group < Struct.new(:source, :vars)
|
11
|
+
def initialize(source, vars)
|
12
|
+
super(source, vars || [])
|
13
|
+
end
|
14
|
+
|
15
|
+
def announce?
|
16
|
+
source != 'travis' && vars.length > 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :data
|
21
|
+
|
22
|
+
def initialize(data)
|
23
|
+
@data = data
|
24
|
+
end
|
25
|
+
|
26
|
+
def vars
|
27
|
+
travis_vars + settings_vars + config_vars
|
28
|
+
end
|
29
|
+
|
30
|
+
def vars_groups
|
31
|
+
[Group.new('travis', travis_vars),
|
32
|
+
Group.new('repository settings', settings_vars),
|
33
|
+
Group.new('.travis.yml', config_vars)]
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def travis_vars
|
39
|
+
to_vars(
|
40
|
+
TRAVIS_PULL_REQUEST: pull_request || false,
|
41
|
+
TRAVIS_SECURE_ENV_VARS: secure_env_vars?,
|
42
|
+
TRAVIS_BUILD_ID: build[:id],
|
43
|
+
TRAVIS_BUILD_NUMBER: build[:number],
|
44
|
+
TRAVIS_BUILD_DIR: [ BUILD_DIR, slug.shellescape ].join('/'),
|
45
|
+
TRAVIS_JOB_ID: job[:id],
|
46
|
+
TRAVIS_JOB_NUMBER: job[:number],
|
47
|
+
TRAVIS_BRANCH: job[:branch].shellescape,
|
48
|
+
TRAVIS_COMMIT: job[:commit],
|
49
|
+
TRAVIS_COMMIT_RANGE: job[:commit_range],
|
50
|
+
TRAVIS_REPO_SLUG: slug.shellescape,
|
51
|
+
TRAVIS_OS_NAME: config[:os],
|
52
|
+
TRAVIS_TAG: job[:tag]
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
def slug
|
57
|
+
repository[:slug] || ''
|
58
|
+
end
|
59
|
+
|
60
|
+
def extract_config_vars(vars)
|
61
|
+
vars = to_vars(Array(vars).compact.reject(&:empty?))
|
62
|
+
vars.reject!(&:secure?) unless secure_env_enabled?
|
63
|
+
vars
|
64
|
+
end
|
65
|
+
|
66
|
+
def config_vars
|
67
|
+
extract_config_vars(config[:global_env]) + extract_config_vars(config[:env])
|
68
|
+
end
|
69
|
+
|
70
|
+
def settings_vars
|
71
|
+
data.raw_env_vars.map { |var| Var.new(var[:name], var[:value], !var[:public]) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_vars(args)
|
75
|
+
args.to_a.map { |args| Var.create(*args) }.flatten
|
76
|
+
end
|
77
|
+
|
78
|
+
def secure_env_vars?
|
79
|
+
secure_env_enabled? && config_vars.any?(&:secure?)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Perkins
|
2
|
+
module Build
|
3
|
+
class Data
|
4
|
+
class Var
|
5
|
+
PATTERN = /(?:SECURE )?([\w]+)=(("|')(.*?)(\3)|\$\(.*?\)|[^"' ]+)/
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def create(*args)
|
9
|
+
if args.size == 1
|
10
|
+
parse(args.first).map { |key, value| Var.new(key, value) }
|
11
|
+
else
|
12
|
+
[Var.new(*args)]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def parse(line)
|
17
|
+
secure = line =~ /^SECURE /
|
18
|
+
line.scan(PATTERN).map { |match| [(secure ? "SECURE #{match[0]}" : match[0]), match[1]] }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :value
|
23
|
+
|
24
|
+
def initialize(key, value, secure = nil)
|
25
|
+
@key = key.to_s
|
26
|
+
@value = value.to_s
|
27
|
+
@secure = secure
|
28
|
+
end
|
29
|
+
|
30
|
+
def key
|
31
|
+
strip_secure(@key)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
if travis?
|
36
|
+
false
|
37
|
+
elsif secure?
|
38
|
+
"export #{[key, '[secure]'].join('=')}"
|
39
|
+
else
|
40
|
+
"export #{[key, value].join('=')}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def travis?
|
45
|
+
@key =~ /^TRAVIS_/
|
46
|
+
end
|
47
|
+
|
48
|
+
def secure?
|
49
|
+
@secure.nil? ? @key =~ /^SECURE / : @secure
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def strip_secure(string)
|
55
|
+
string.gsub('SECURE ', '')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
require 'core_ext/hash/deep_merge'
|
2
|
+
require 'core_ext/hash/deep_symbolize_keys'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
# actually, the worker payload can be cleaned up a lot ...
|
6
|
+
|
7
|
+
module Perkins
|
8
|
+
module Build
|
9
|
+
class Data
|
10
|
+
autoload :Env, 'perkins/build/data/env'
|
11
|
+
autoload :Var, 'perkins/build/data/var'
|
12
|
+
|
13
|
+
DEFAULTS = { }
|
14
|
+
|
15
|
+
DEFAULT_CACHES = {
|
16
|
+
apt: false,
|
17
|
+
bundler: false,
|
18
|
+
cocoapods: false,
|
19
|
+
composer: false
|
20
|
+
}
|
21
|
+
|
22
|
+
attr_reader :data
|
23
|
+
|
24
|
+
def initialize(data, defaults = {})
|
25
|
+
data = data.deep_symbolize_keys
|
26
|
+
defaults = defaults.deep_symbolize_keys
|
27
|
+
@data = DEFAULTS.deep_merge(defaults.deep_merge(data))
|
28
|
+
end
|
29
|
+
|
30
|
+
def urls
|
31
|
+
data[:urls] || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def config
|
35
|
+
data[:config]
|
36
|
+
end
|
37
|
+
|
38
|
+
def hosts
|
39
|
+
data[:hosts] || {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def paranoid_mode?
|
43
|
+
data.fetch(:paranoid, false)
|
44
|
+
end
|
45
|
+
|
46
|
+
def skip_resolv_updates?
|
47
|
+
data.fetch(:skip_resolv_updates, false)
|
48
|
+
end
|
49
|
+
|
50
|
+
def skip_etc_hosts_fix?
|
51
|
+
data.fetch(:skip_etc_hosts_fix, false)
|
52
|
+
end
|
53
|
+
|
54
|
+
def cache_options
|
55
|
+
data[:cache_options] || {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def cache(input = config[:cache])
|
59
|
+
case input
|
60
|
+
when Hash then input
|
61
|
+
when Array then input.map { |e| cache(e) }.inject(:merge)
|
62
|
+
when String, Symbol then { input.to_sym => true }
|
63
|
+
when nil then {} # for ruby 1.9
|
64
|
+
when false then Hash[DEFAULT_CACHES.each_key.with_object(false).to_a]
|
65
|
+
else input.to_h
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def cache?(type, default = DEFAULT_CACHES[type])
|
70
|
+
type &&= type.to_sym
|
71
|
+
!!cache.fetch(type) { default }
|
72
|
+
end
|
73
|
+
|
74
|
+
def env_vars
|
75
|
+
@env_vars ||= Env.new(self).vars
|
76
|
+
end
|
77
|
+
|
78
|
+
def env_vars_groups
|
79
|
+
@env_vars_groups ||= Env.new(self).vars_groups
|
80
|
+
end
|
81
|
+
|
82
|
+
def raw_env_vars
|
83
|
+
data[:env_vars] || []
|
84
|
+
end
|
85
|
+
|
86
|
+
class SshKey < Struct.new(:value, :source, :encoded)
|
87
|
+
def value
|
88
|
+
if encoded?
|
89
|
+
Base64.decode64(super)
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def encoded?
|
96
|
+
encoded
|
97
|
+
end
|
98
|
+
|
99
|
+
def fingerprint
|
100
|
+
rsa_key = OpenSSL::PKey::RSA.new(value)
|
101
|
+
public_ssh_rsa = "\x00\x00\x00\x07ssh-rsa" + rsa_key.e.to_s(0) + rsa_key.n.to_s(0)
|
102
|
+
OpenSSL::Digest::MD5.new(public_ssh_rsa).hexdigest.scan(/../).join(':')
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def ssh_key
|
107
|
+
if ssh_key = data[:ssh_key]
|
108
|
+
SshKey.new(ssh_key[:value], ssh_key[:source], ssh_key[:encoded])
|
109
|
+
elsif source_key = data[:config][:source_key]
|
110
|
+
SshKey.new(source_key, nil, true)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def pull_request
|
115
|
+
job[:pull_request]
|
116
|
+
end
|
117
|
+
|
118
|
+
def secure_env_enabled?
|
119
|
+
job[:secure_env_enabled]
|
120
|
+
end
|
121
|
+
|
122
|
+
def source_host
|
123
|
+
source_url =~ %r(^(?:https?|git)(?:://|@)([^/]*?)(?:/|:)) && $1
|
124
|
+
end
|
125
|
+
|
126
|
+
def api_url
|
127
|
+
repository[:api_url]
|
128
|
+
end
|
129
|
+
|
130
|
+
def source_url
|
131
|
+
repository[:source_url]
|
132
|
+
end
|
133
|
+
|
134
|
+
def slug
|
135
|
+
repository[:slug]
|
136
|
+
end
|
137
|
+
|
138
|
+
def commit
|
139
|
+
job[:commit]
|
140
|
+
end
|
141
|
+
|
142
|
+
def branch
|
143
|
+
job[:branch]
|
144
|
+
end
|
145
|
+
|
146
|
+
def ref
|
147
|
+
job[:ref]
|
148
|
+
end
|
149
|
+
|
150
|
+
def job
|
151
|
+
data[:job] || {}
|
152
|
+
end
|
153
|
+
|
154
|
+
def build
|
155
|
+
data[:source] || data[:build] || {} # TODO standarize the payload on :build
|
156
|
+
end
|
157
|
+
|
158
|
+
def repository
|
159
|
+
data[:repository] || {}
|
160
|
+
end
|
161
|
+
|
162
|
+
def token
|
163
|
+
data[:oauth_token]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|