Merge branch 'master', remote-tracking branch 'origin'

This commit is contained in:
Brad 2014-06-04 13:23:46 -07:00
commit 040ef6f8ab
20 changed files with 1211 additions and 1198 deletions

View file

@ -3,4 +3,6 @@
Artur Rodrigues <arturhoo@gmail.com> Artur Rodrigues <arturhoo@gmail.com>
Brad Rydzewski <brad@drone.io> Brad Rydzewski <brad@drone.io>
Martin Charles <martincharles07@gmail.com>
Thomas Burke <burke@drone.io> Thomas Burke <burke@drone.io>

View file

@ -1,14 +1,14 @@
body { body {
background: #FFF; background: #FFF;
} }
.container { .container {
max-width: none !important; max-width: none !important;
width: 940px; width: 940px;
padding: 0px; padding: 0;
} }
.row { .row {
margin: 0px; margin: 0;
padding: 0px; padding: 0;
} }
.col-xs-1, .col-xs-1,
.col-xs-2, .col-xs-2,
@ -19,7 +19,7 @@
.col-xs-7, .col-xs-7,
.col-xs-8, .col-xs-8,
.col-xs-9 { .col-xs-9 {
padding: 0px; padding: 0;
} }
.row > .col-xs-2:last-child, .row > .col-xs-2:last-child,
.row > .col-xs-3:last-child, .row > .col-xs-3:last-child,
@ -36,7 +36,7 @@
margin-bottom: 0px; margin-bottom: 0px;
} }
.navbar-inverse .container { .navbar-inverse .container {
padding-right: 0px; padding-right: 0;
} }
.navbar-fixed-top, .navbar-fixed-top,
.navbar-fixed-bottom { .navbar-fixed-bottom {
@ -53,7 +53,7 @@
} }
.navbar .nav { .navbar .nav {
float: right; float: right;
margin-right: 0px; margin-right: 0;
} }
.navbar .nav > li > a { .navbar .nav > li > a {
padding: 18px 20px; padding: 18px 20px;
@ -63,10 +63,9 @@
margin: 0px; margin: 0px;
margin-top: 11px; margin-top: 11px;
border-radius: 4px; border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
background: #363839; background: #363839;
border: none; border: none;
-webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
text-shadow: none; text-shadow: none;
color: #DDD; color: #DDD;
@ -82,7 +81,6 @@
border: none; border: none;
position: relative; position: relative;
-webkit-box-shadow: none; -webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none; box-shadow: none;
} }
.subhead h1 { .subhead h1 {
@ -110,8 +108,6 @@
width: 42px; width: 42px;
height: 42px; height: 42px;
border-radius: 50%; border-radius: 50%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
} }
.subhead h1.user small { .subhead h1.user small {
color: #777; color: #777;
@ -119,14 +115,14 @@
} }
.subhead .container { .subhead .container {
position: relative; position: relative;
padding: 0px; padding: 0;
} }
.subhead .nav-tabs { .subhead .nav-tabs {
position: absolute; position: absolute;
right: 0px; right: 0;
bottom: 0px; bottom: 0;
margin: 0px; margin: 0;
padding: 0px; padding: 0;
border-bottom: none; border-bottom: none;
} }
.subhead .nav-tabs > li { .subhead .nav-tabs > li {
@ -136,11 +132,11 @@
color: #999; color: #999;
padding: 10px 20px; padding: 10px 20px;
line-height: 20px; line-height: 20px;
border: 0px; border: 0;
} }
.subhead .nav-tabs > li.active > a { .subhead .nav-tabs > li.active > a {
color: #777; color: #777;
border: 0px; border: 0;
} }
.nav-repos, .nav-repos,
.nav-branches { .nav-branches {
@ -154,11 +150,9 @@
padding: 5px 15px; padding: 5px 15px;
padding: 10px 15px; padding: 10px 15px;
border: none; border: none;
margin: 0px !IMPORTANT; margin: 0 !IMPORTANT;
font-size: 15px; font-size: 15px;
border-radius: 0px; border-radius: 0;
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
overflow: hidden; overflow: hidden;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
@ -200,7 +194,7 @@
.nav-repos li a, .nav-repos li a,
.nav-branches li a { .nav-branches li a {
padding: 12px 15px; padding: 12px 15px;
margin-bottom: 0px; margin-bottom: 0;
color: #777; color: #777;
} }
.nav-pills.nav-repos > li > a, .nav-pills.nav-repos > li > a,
@ -233,12 +227,12 @@
margin-top: -10px; margin-top: -10px;
} }
.alert .thumbnails { .alert .thumbnails {
margin: 0px; margin: 0;
padding: 0px; padding: 0;
} }
.alert .thumbnails li { .alert .thumbnails li {
margin: 0px; margin: 0;
padding: 0px; padding: 0;
margin-left: 20px; margin-left: 20px;
margin-top: 20px; margin-top: 20px;
} }
@ -261,9 +255,9 @@
font-weight: normal; font-weight: normal;
} }
.alert.alert-feed .thumbnails > li { .alert.alert-feed .thumbnails > li {
margin: 0px; margin: 0;
margin-left: 5px; margin-left: 5px;
padding: 0px; padding: 0;
display: inline-block; display: inline-block;
} }
.alert.alert-feed .thumbnails > li a { .alert.alert-feed .thumbnails > li a {
@ -272,8 +266,6 @@
} }
.alert.alert-feed .thumbnails > li a img { .alert.alert-feed .thumbnails > li a img {
border-radius: 50%; border-radius: 50%;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
width: 32px; width: 32px;
height: 32px; height: 32px;
margin-top: 10px; margin-top: 10px;
@ -296,11 +288,11 @@
} }
.commit-list { .commit-list {
list-style: none; list-style: none;
margin: 0px; margin: 0;
padding-left: 0px; padding-left: 0;
} }
.commit-list > li { .commit-list > li {
margin: 0px; margin: 0;
position: relative; position: relative;
padding: 25px 20px; padding: 25px 20px;
padding-left: 25px; padding-left: 25px;
@ -310,8 +302,6 @@
position: absolute; position: absolute;
right: 20px; right: 20px;
top: 20px; top: 20px;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%; border-radius: 50%;
width: 48px; width: 48px;
height: 48px; height: 48px;
@ -359,8 +349,8 @@
.commit-list > li > h3 p { .commit-list > li > h3 p {
font-size: 14px; font-size: 14px;
color: #999; color: #999;
margin: 0px; margin: 0;
padding: 0px; padding: 0;
padding-top: 2px; padding-top: 2px;
display: block; display: block;
overflow: hidden; overflow: hidden;
@ -378,6 +368,7 @@
background: #BBB; background: #BBB;
color: #FFF; color: #FFF;
text-shadow: none; text-shadow: none;
-webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
padding: 10px 15px; padding: 10px 15px;
min-width: 75px; min-width: 75px;
@ -395,8 +386,6 @@
min-width: 48px; min-width: 48px;
max-width: 48px; max-width: 48px;
border-radius: 50%; border-radius: 50%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
} }
.btn.btn-success, .btn.btn-success,
.btn.btn-Success { .btn.btn-Success {
@ -436,8 +425,6 @@
background: #f7f7f7; background: #f7f7f7;
width: 75px; width: 75px;
border-radius: 0px; border-radius: 0px;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
text-align: center; text-align: center;
font-size: 22px; font-size: 22px;
text-decoration: none; text-decoration: none;
@ -460,9 +447,6 @@
color: #FFF; color: #FFF;
display: inline-block; display: inline-block;
-webkit-animation: spin 1.5s infinite linear; -webkit-animation: spin 1.5s infinite linear;
-moz-animation: spin 1.5s infinite linear;
-ms-animation: spin 1.5s infinite linear;
-o-animation: spin 1.5s infinite linear;
animation: spin 1.5s infinite linear; animation: spin 1.5s infinite linear;
} }
.btn.btn-mini { .btn.btn-mini {
@ -482,31 +466,6 @@
line-height: 24px !IMPORTANT; line-height: 24px !IMPORTANT;
font-size: 14px !IMPORTANT; font-size: 14px !IMPORTANT;
} }
@-webkit-keyframes spin {
to {
-webkit-transform: rotate(360deg);
}
}
@-moz-keyframes spin {
to {
-moz-transform: rotate(360deg);
}
}
@-ms-keyframes spin {
to {
-ms-transform: rotate(360deg);
}
}
@-o-keyframes spin {
to {
-o-transform: rotate(360deg);
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.btn.btn-None { .btn.btn-None {
background: rgba(81, 163, 81, 0.75); background: rgba(81, 163, 81, 0.75);
border-bottom: 2px solid #51a351; border-bottom: 2px solid #51a351;
@ -539,6 +498,18 @@
background: rgba(189, 54, 47, 0.9); background: rgba(189, 54, 47, 0.9);
border-bottom: 2px solid #bd362f; border-bottom: 2px solid #bd362f;
} }
@-webkit-keyframes spin {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes spin {
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
.nav-pills > li a { .nav-pills > li a {
padding: 7px 15px; padding: 7px 15px;
color: #777777; color: #777777;
@ -546,14 +517,10 @@
.nav-pills > li > a, .nav-pills > li > a,
.nav-pills > li > a:hover, .nav-pills > li > a:hover,
.nav-pills > li > a:focus { .nav-pills > li > a:focus {
border-radius: 0px; border-radius: 0;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
} }
.nav-pills > .active { .nav-pills > .active {
border-radius: 0px; border-radius: 0;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
} }
.nav-pills > li.active > a, .nav-pills > li.active > a,
.nav-pills > li.active > a:hover, .nav-pills > li.active > a:hover,
@ -561,10 +528,8 @@
.nav-pills > .active > a, .nav-pills > .active > a,
.nav-pills > .active > a:hover, .nav-pills > .active > a:hover,
.nav-pills > .active > a:focus { .nav-pills > .active > a:focus {
border-radius: 0px; border-radius: 0;
-webkit-border-radius: 0px; margin-top: 0;
-moz-border-radius: 0px;
margin-top: 0px;
background-color: #f4f4f4; background-color: #f4f4f4;
color: #777777; color: #777777;
} }
@ -578,12 +543,12 @@ form label {
display: block; display: block;
} }
form label:first-child { form label:first-child {
margin-top: 0px; margin-top: 0;
} }
form .form-actions { form .form-actions {
background: transparent; background: transparent;
padding: 30px 0px; padding: 30px 0px;
margin: 0px; margin: 0;
border: none; border: none;
} }
form .form-actions .btn { form .form-actions .btn {
@ -601,8 +566,6 @@ select.form-control {
font-size: 14px; font-size: 14px;
line-height: 20px; line-height: 20px;
color: #555555; color: #555555;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px; border-radius: 4px;
vertical-align: middle; vertical-align: middle;
width: 270px; width: 270px;
@ -624,7 +587,6 @@ span.form-control {
background-color: #fcfcfc; background-color: #fcfcfc;
border-color: #cccccc; border-color: #cccccc;
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
-moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
cursor: not-allowed; cursor: not-allowed;
} }
@ -666,22 +628,21 @@ textarea {
font-size: 16px; font-size: 16px;
} }
.row.gravatar-list { .row.gravatar-list {
margin-left: 0px; margin-left: 0;
} }
.row.gravatar-list .col-xs-3 { .row.gravatar-list .col-xs-3 {
width: 23.404255319148934%; width: 23.404255319148934%;
margin: 0px; margin: 0;
padding: 0px; padding: 0;
margin-right: 2.127659574468085%; margin-right: 2.127659574468085%;
} }
.row.gravatar-list .col-xs-3:nth-child(4) { .row.gravatar-list .col-xs-3:nth-child(4) {
margin-right: 0px; margin-right: 0;
} }
.row.gravatar-list .col-xs-3 a.thumbnail, .row.gravatar-list .col-xs-3 a.thumbnail,
.row.gravatar-list .col-xs-3 a.thumbnail:hover { .row.gravatar-list .col-xs-3 a.thumbnail:hover {
border-radius: 0px; border-radius: 0;
-webkit-border-radius: 0px; -webkit-box-shadow: none;
-moz-border-radius: 0px;
box-shadow: none; box-shadow: none;
border: none; border: none;
text-decoration: none; text-decoration: none;
@ -721,7 +682,11 @@ textarea {
.row.gravatar-list .col-xs-3 a.thumbnail:hover.team-add .caption > h3 { .row.gravatar-list .col-xs-3 a.thumbnail:hover.team-add .caption > h3 {
color: #999; color: #999;
} }
/* Checkbox hack */ .switch {
height: 40px;
position: relative;
width: 100px;
}
.switch input, .switch input,
.switch a { .switch a {
cursor: pointer; cursor: pointer;
@ -731,25 +696,18 @@ textarea {
width: 100%; width: 100%;
z-index: 100; z-index: 100;
} }
/* Container */
.switch {
height: 40px;
position: relative;
width: 100px;
}
/* Background unchecked */
.switch label { .switch label {
background: rgba(189, 54, 47, 0.8); background: rgba(189, 54, 47, 0.8);
border-radius: 2px; border-radius: 2px;
display: block; display: block;
height: 100%; height: 100%;
position: relative; position: relative;
-webkit-transition: all .15s ease;
transition: all .15s ease; transition: all .15s ease;
width: 100%; width: 100%;
border-radius: 5px; border-radius: 5px;
color: #FFF; color: #FFF;
} }
/* Slider unchecked */
.switch label div { .switch label div {
background: #fff; background: #fff;
border-radius: 2px; border-radius: 2px;
@ -758,12 +716,12 @@ textarea {
left: 6px; left: 6px;
position: absolute; position: absolute;
top: 6px; top: 6px;
-webkit-transition: all .15s ease;
transition: all .15s ease; transition: all .15s ease;
width: 35px; width: 35px;
z-index: 3; z-index: 3;
border-radius: 5px; border-radius: 5px;
} }
/* Vertical lines on slider */
.switch label div:before { .switch label div:before {
bottom: 0; bottom: 0;
content: ''; content: '';
@ -774,10 +732,10 @@ textarea {
position: absolute; position: absolute;
right: 0; right: 0;
top: 0; top: 0;
-webkit-transition: all .15s ease;
transition: all .15s ease; transition: all .15s ease;
width: 3px; width: 3px;
} }
/* Icon styles */
.switch .fontawesome-ok { .switch .fontawesome-ok {
font-size: 18px; font-size: 18px;
left: 15px; left: 15px;
@ -802,7 +760,6 @@ textarea {
content: "\f00d"; content: "\f00d";
opacity: 0.8; opacity: 0.8;
} }
/* Checked States */
.switch input:checked ~ label { .switch input:checked ~ label {
background: rgba(81, 163, 81, 0.75); background: rgba(81, 163, 81, 0.75);
} }
@ -832,8 +789,6 @@ pre {
font-size: 15px; font-size: 15px;
padding: 15px 20px; padding: 15px 20px;
border-radius: 5px; border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
} }
.alert.alert-build-Success span, .alert.alert-build-Success span,
.alert.alert-build-Error span, .alert.alert-build-Error span,
@ -870,6 +825,14 @@ pre {
font-size: 22px !IMPORTANT; font-size: 22px !IMPORTANT;
line-height: 32px !IMPORTANT; line-height: 32px !IMPORTANT;
} }
.alert.alert-build-Success .actions,
.alert.alert-build-Error .actions,
.alert.alert-build-Failure .actions,
.alert.alert-build-Pending .actions,
.alert.alert-build-Started .actions {
float: right;
margin-top: -2px;
}
.build-details { .build-details {
background: #FFF; background: #FFF;
margin-bottom: 40px; margin-bottom: 40px;
@ -894,8 +857,6 @@ pre {
} }
.build-details img { .build-details img {
float: left; float: left;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%; border-radius: 50%;
margin-right: 30px; margin-right: 30px;
width: 58px; width: 58px;
@ -936,7 +897,7 @@ pre {
margin-bottom: 30px; margin-bottom: 30px;
} }
.form-repo .field-group label { .form-repo .field-group label {
margin-top: 0px; margin-top: 0;
} }
.form-repo .field-separator { .form-repo .field-separator {
display: inline-block; display: inline-block;
@ -944,11 +905,11 @@ pre {
} }
.form-repo ul { .form-repo ul {
padding-top: 15px; padding-top: 15px;
margin-bottom: 0px; margin-bottom: 0;
padding-bottom: 0px; padding-bottom: 0;
list-style: none; list-style: none;
padding-left: 0px; padding-left: 0;
margin-left: 0px; margin-left: 0;
} }
.form-repo ul li { .form-repo ul li {
padding-bottom: 10px; padding-bottom: 10px;
@ -957,18 +918,16 @@ pre {
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: 50%; border-radius: 50%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
} }
ul.account-radio-group { ul.account-radio-group {
padding-top: 15px; padding-top: 15px;
margin-bottom: 0px; margin-bottom: 0;
padding-bottom: 0px; padding-bottom: 0;
list-style: none; list-style: none;
padding-left: 0px; padding-left: 0;
margin-left: 0px; margin-left: 0;
} }
ul.account-radio-group li { ul.account-radio-group li {
padding-bottom: 10px; padding-bottom: 10px;
@ -977,8 +936,6 @@ ul.account-radio-group li img {
width: 32px; width: 32px;
height: 32px; height: 32px;
border-radius: 50%; border-radius: 50%;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
} }
@ -991,8 +948,6 @@ ul.account-radio-group li img {
margin-top: 40px; margin-top: 40px;
padding: 0px 0px 20px 0px; padding: 0px 0px 20px 0px;
border-radius: 5px; border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
} }
.form-centered h1 { .form-centered h1 {
background-color: #262829; background-color: #262829;
@ -1003,7 +958,7 @@ ul.account-radio-group li img {
font-weight: normal; font-weight: normal;
font-size: 18px; font-size: 18px;
padding: 25px 10px; padding: 25px 10px;
margin-top: 0px; margin-top: 0;
margin-bottom: 30px; margin-bottom: 30px;
} }
.form-centered input[type="submit"] { .form-centered input[type="submit"] {
@ -1016,8 +971,6 @@ ul.account-radio-group li img {
width: 280px; width: 280px;
background: #AAA; background: #AAA;
border-radius: 5px; border-radius: 5px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
} }
.form-centered input[type="text"], .form-centered input[type="text"],
.form-centered input[type="email"], .form-centered input[type="email"],
@ -1044,6 +997,7 @@ ul.account-radio-group li img {
.form-centered input[type="text"][disabled], .form-centered input[type="text"][disabled],
.form-centered input[type="email"][disabled], .form-centered input[type="email"][disabled],
.form-centered input[type="password"][disabled] { .form-centered input[type="password"][disabled] {
-webkit-box-shadow: none;
box-shadow: none; box-shadow: none;
background: #f7f7f7; background: #f7f7f7;
color: #999; color: #999;

File diff suppressed because it is too large Load diff

View file

@ -122,7 +122,7 @@ func setupStatic() {
w.Header().Add("Cache-Control", "no-cache") w.Header().Add("Cache-Control", "no-cache")
} }
// serce images // serve images
images.ServeHTTP(w, r) images.ServeHTTP(w, r)
}) })
} }
@ -136,6 +136,7 @@ func setupHandlers() {
github = handler.NewGithubHandler(queue) github = handler.NewGithubHandler(queue)
gitlab = handler.NewGitlabHandler(queue) gitlab = handler.NewGitlabHandler(queue)
bitbucket = handler.NewBitbucketHandler(queue) bitbucket = handler.NewBitbucketHandler(queue)
rebuild = handler.NewCommitRebuildHandler(queue)
) )
m := pat.New() m := pat.New()
@ -226,7 +227,9 @@ func setupHandlers() {
// handlers for repository, commits and build details // handlers for repository, commits and build details
m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut)) m.Get("/:host/:owner/:name/commit/:commit/build/:label/out.txt", handler.RepoHandler(handler.BuildOut))
m.Post("/:host/:owner/:name/commit/:commit/build/:label/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))
m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow)) m.Get("/:host/:owner/:name/commit/:commit/build/:label", handler.RepoHandler(handler.CommitShow))
m.Post("/:host/:owner/:name/commit/:commit/rebuild", handler.RepoAdminHandler(rebuild.CommitRebuild))
m.Get("/:host/:owner/:name/commit/:commit", handler.RepoHandler(handler.CommitShow)) m.Get("/:host/:owner/:name/commit/:commit", handler.RepoHandler(handler.CommitShow))
m.Get("/:host/:owner/:name/tree", handler.RepoHandler(handler.RepoDashboard)) m.Get("/:host/:owner/:name/tree", handler.RepoHandler(handler.RepoDashboard))
m.Get("/:host/:owner/:name/status.svg", handler.ErrorHandler(handler.Badge)) m.Get("/:host/:owner/:name/status.svg", handler.ErrorHandler(handler.Badge))

View file

@ -10,7 +10,7 @@ const buildTable = "builds"
// SQL Queries to retrieve a list of all Commits belonging to a Repo. // SQL Queries to retrieve a list of all Commits belonging to a Repo.
const buildStmt = ` const buildStmt = `
SELECT id, commit_id, slug, status, started, finished, duration, created, updated, stdout SELECT id, commit_id, slug, status, started, finished, duration, created, updated, stdout, buildscript
FROM builds FROM builds
WHERE commit_id = ? WHERE commit_id = ?
ORDER BY slug ASC ORDER BY slug ASC
@ -18,7 +18,7 @@ ORDER BY slug ASC
// SQL Queries to retrieve a Build by id. // SQL Queries to retrieve a Build by id.
const buildFindStmt = ` const buildFindStmt = `
SELECT id, commit_id, slug, status, started, finished, duration, created, updated, stdout SELECT id, commit_id, slug, status, started, finished, duration, created, updated, stdout, buildscript
FROM builds FROM builds
WHERE id = ? WHERE id = ?
LIMIT 1 LIMIT 1
@ -26,7 +26,7 @@ LIMIT 1
// SQL Queries to retrieve a Commit by name and repo id. // SQL Queries to retrieve a Commit by name and repo id.
const buildFindSlugStmt = ` const buildFindSlugStmt = `
SELECT id, commit_id, slug, status, started, finished, duration, created, updated, stdout SELECT id, commit_id, slug, status, started, finished, duration, created, updated, stdout, buildscript
FROM builds FROM builds
WHERE slug = ? AND commit_id = ? WHERE slug = ? AND commit_id = ?
LIMIT 1 LIMIT 1

View file

@ -0,0 +1,20 @@
package migrate
type rev20140522205400 struct{}
var SaveDroneYml = &rev20140522205400{}
func (r *rev20140522205400) Revision() int64 {
return 20140522205400
}
func (r *rev20140522205400) Up(mg *MigrationDriver) error {
_, err := mg.AddColumn("builds", "buildscript TEXT")
_, err = mg.Tx.Exec("UPDATE builds SET buildscript = '' WHERE buildscript IS NULL")
return err
}
func (r *rev20140522205400) Down(mg *MigrationDriver) error {
_, err := mg.DropColumns("builds", "buildscript")
return err
}

View file

@ -13,6 +13,7 @@ func (m *Migration) All() *Migration {
m.Add(GitHubEnterpriseSupport) m.Add(GitHubEnterpriseSupport)
m.Add(AddOpenInvitationColumn) m.Add(AddOpenInvitationColumn)
m.Add(AddGitlabColumns) m.Add(AddGitlabColumns)
m.Add(SaveDroneYml)
// m.Add(...) // m.Add(...)
// ... // ...

View file

@ -90,3 +90,18 @@ func ListReposTeam(id int64) ([]*Repo, error) {
err := meddler.QueryAll(db, &repos, repoTeamStmt, id) err := meddler.QueryAll(db, &repos, repoTeamStmt, id)
return repos, err return repos, err
} }
// Checks whether a user is admin of a repo
// Returns true if user owns repo or is on team that owns repo
// Returns true if the user is an admin member of the team.
func IsRepoAdmin(user *User, repo *Repo) (bool, error) {
if user == nil {
return false, nil
}
if user.ID == repo.UserID {
return true, nil
}
return IsMemberAdmin(user.ID, repo.TeamID)
}

View file

@ -43,26 +43,29 @@ func Badge(w http.ResponseWriter, r *http.Request) error {
// get the latest commit from the database // get the latest commit from the database
// for the requested branch // for the requested branch
commit, err := database.GetBranch(repo.ID, branchParam) commit, err := database.GetBranch(repo.ID, branchParam)
if err == nil { if err != nil {
switch { http.NotFound(w, r)
case commit.Status == "Success" && len(successParam) == 0: return nil
// if no success image is provided, we serve a }
// badge using the shields.io service
badge = badgeSuccess switch {
case commit.Status == "Success" && len(successParam) != 0: case commit.Status == "Success" && len(successParam) == 0:
// otherwise we serve the user defined success badge // if no success image is provided, we serve a
badge = successParam // badge using the shields.io service
case commit.Status == "Failure" && len(failureParam) == 0: badge = badgeSuccess
// if no failure image is provided, we serve a case commit.Status == "Success" && len(successParam) != 0:
// badge using the shields.io service // otherwise we serve the user defined success badge
badge = badgeFailure badge = successParam
case commit.Status == "Failure" && len(failureParam) != 0: case commit.Status == "Failure" && len(failureParam) == 0:
// otherwise we serve the user defined failure badge // if no failure image is provided, we serve a
badge = failureParam // badge using the shields.io service
default: badge = badgeFailure
// otherwise load unknown image case commit.Status == "Failure" && len(failureParam) != 0:
badge = badgeUnknown // otherwise we serve the user defined failure badge
} badge = failureParam
default:
// otherwise load unknown image
badge = badgeUnknown
} }
http.Redirect(w, r, badge, http.StatusSeeOther) http.Redirect(w, r, badge, http.StatusSeeOther)

View file

@ -5,7 +5,6 @@ import (
"net/http" "net/http"
"time" "time"
"github.com/drone/drone/pkg/build/script"
"github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database"
. "github.com/drone/drone/pkg/model" . "github.com/drone/drone/pkg/model"
"github.com/drone/drone/pkg/queue" "github.com/drone/drone/pkg/queue"
@ -83,16 +82,6 @@ func (h *BitbucketHandler) Hook(w http.ResponseWriter, r *http.Request) error {
return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return RenderText(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
} }
// parse the build script
buildscript, err := script.ParseBuild([]byte(raw.Data), repo.Params)
if err != nil {
msg := "Could not parse your .drone.yml file. It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
if err := saveFailedBuild(commit, msg); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}
// save the commit to the database // save the commit to the database
if err := database.SaveCommit(commit); err != nil { if err := database.SaveCommit(commit); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -104,12 +93,13 @@ func (h *BitbucketHandler) Hook(w http.ResponseWriter, r *http.Request) error {
build.CommitID = commit.ID build.CommitID = commit.ID
build.Created = time.Now().UTC() build.Created = time.Now().UTC()
build.Status = "Pending" build.Status = "Pending"
build.BuildScript = raw.Data
if err := database.SaveBuild(build); err != nil { if err := database.SaveBuild(build); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
// send the build to the queue // send the build to the queue
h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript}) h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})
// OK! // OK!
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK) return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)

View file

@ -8,6 +8,7 @@ import (
"github.com/drone/drone/pkg/channel" "github.com/drone/drone/pkg/channel"
"github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database"
. "github.com/drone/drone/pkg/model" . "github.com/drone/drone/pkg/model"
"github.com/drone/drone/pkg/queue"
) )
// Display a specific Commit. // Display a specific Commit.
@ -33,14 +34,20 @@ func CommitShow(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) err
return err return err
} }
admin, err := database.IsRepoAdmin(u, repo)
if err != nil {
return err
}
data := struct { data := struct {
User *User User *User
Repo *Repo Repo *Repo
Commit *Commit Commit *Commit
Build *Build Build *Build
Builds []*Build Builds []*Build
Token string Token string
}{u, repo, commit, builds[0], builds, ""} IsAdmin bool
}{u, repo, commit, builds[0], builds, "", admin}
// get the specific build requested by the user. instead // get the specific build requested by the user. instead
// of a database round trip, we can just loop through the // of a database round trip, we can just loop through the
@ -94,3 +101,70 @@ func saveFailedBuild(commit *Commit, msg string) error {
return nil return nil
} }
type CommitRebuildHandler struct {
queue *queue.Queue
}
func NewCommitRebuildHandler(queue *queue.Queue) *CommitRebuildHandler {
return &CommitRebuildHandler{
queue: queue,
}
}
// CommitRebuild re-queues a previously built commit. It finds the existing
// commit and build and injects them back into the queue. If the commit
// doesn't exist or has no builds, or if a build label has been passed but
// can't be located, it prints an error. Otherwise, it adds the build/commit
// to the queue and redirects back to the commit page.
func (h *CommitRebuildHandler) CommitRebuild(w http.ResponseWriter, r *http.Request, u *User, repo *Repo) error {
hash := r.FormValue(":commit")
labl := r.FormValue(":label")
host := r.FormValue(":host")
branch := r.FormValue("branch")
if branch == "" {
branch = "master"
}
// get the commit from the database
commit, err := database.GetCommitBranchHash(branch, hash, repo.ID)
if err != nil {
return err
}
// get the builds from the database. a commit can have
// multiple sub-builds (or matrix builds)
builds, err := database.ListBuilds(commit.ID)
if err != nil {
return err
}
build := builds[0]
if labl != "" {
// get the specific build requested by the user. instead
// of a database round trip, we can just loop through the
// list and extract the requested build.
build = nil
for _, b := range builds {
if b.Slug == labl {
build = b
break
}
}
}
if build == nil {
return fmt.Errorf("Could not find build: %s", labl)
}
h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})
if labl != "" {
http.Redirect(w, r, fmt.Sprintf("/%s/%s/%s/commit/%s/build/%s?branch=%s", host, repo.Owner, repo.Name, hash, labl, branch), http.StatusSeeOther)
} else {
http.Redirect(w, r, fmt.Sprintf("/%s/%s/%s/commit/%s?branch=%s", host, repo.Owner, repo.Name, hash, branch), http.StatusSeeOther)
}
return nil
}

View file

@ -6,7 +6,6 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/drone/drone/pkg/build/script"
"github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database"
. "github.com/drone/drone/pkg/model" . "github.com/drone/drone/pkg/model"
"github.com/drone/drone/pkg/queue" "github.com/drone/drone/pkg/queue"
@ -131,22 +130,8 @@ func (h *GithubHandler) Hook(w http.ResponseWriter, r *http.Request) error {
} }
// decode the content. Note: Not sure this will ever happen...it basically means a GitHub API issue // decode the content. Note: Not sure this will ever happen...it basically means a GitHub API issue
raw, err := content.DecodeContent() buildscript, err := content.DecodeContent()
if err != nil { if err != nil {
msg := "Could not decode the yaml from GitHub. Check that your .drone.yml is a valid yaml file.\n"
if err := saveFailedBuild(commit, msg); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}
// parse the build script
buildscript, err := script.ParseBuild(raw, repo.Params)
if err != nil {
msg := "Could not parse your .drone.yml file. It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
if err := saveFailedBuild(commit, msg); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
} }
@ -161,6 +146,7 @@ func (h *GithubHandler) Hook(w http.ResponseWriter, r *http.Request) error {
build.CommitID = commit.ID build.CommitID = commit.ID
build.Created = time.Now().UTC() build.Created = time.Now().UTC()
build.Status = "Pending" build.Status = "Pending"
build.BuildScript = string(buildscript)
if err := database.SaveBuild(build); err != nil { if err := database.SaveBuild(build); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
@ -169,7 +155,7 @@ func (h *GithubHandler) Hook(w http.ResponseWriter, r *http.Request) error {
//realtime.CommitPending(repo.UserID, repo.TeamID, repo.ID, commit.ID, repo.Private) //realtime.CommitPending(repo.UserID, repo.TeamID, repo.ID, commit.ID, repo.Private)
//realtime.BuildPending(repo.UserID, repo.TeamID, repo.ID, commit.ID, build.ID, repo.Private) //realtime.BuildPending(repo.UserID, repo.TeamID, repo.ID, commit.ID, build.ID, repo.Private)
h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript}) //Push(repo, commit, build, buildscript) h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build}) //Push(repo, commit, build)
// OK! // OK!
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK) return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
@ -250,22 +236,12 @@ func (h *GithubHandler) PullRequestHook(w http.ResponseWriter, r *http.Request)
} }
// decode the content // decode the content
raw, err := content.DecodeContent() buildscript, err := content.DecodeContent()
if err != nil { if err != nil {
RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return return
} }
// parse the build script
buildscript, err := script.ParseBuild(raw, repo.Params)
if err != nil {
// TODO if the YAML is invalid we should create a commit record
// with an ERROR status so that the user knows why a build wasn't
// triggered in the system
RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
return
}
// save the commit to the database // save the commit to the database
if err := database.SaveCommit(commit); err != nil { if err := database.SaveCommit(commit); err != nil {
RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -278,6 +254,7 @@ func (h *GithubHandler) PullRequestHook(w http.ResponseWriter, r *http.Request)
build.CommitID = commit.ID build.CommitID = commit.ID
build.Created = time.Now().UTC() build.Created = time.Now().UTC()
build.Status = "Pending" build.Status = "Pending"
build.BuildScript = string(buildscript)
if err := database.SaveBuild(build); err != nil { if err := database.SaveBuild(build); err != nil {
RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
return return
@ -285,7 +262,7 @@ func (h *GithubHandler) PullRequestHook(w http.ResponseWriter, r *http.Request)
// notify websocket that a new build is pending // notify websocket that a new build is pending
// TODO we should, for consistency, just put this inside Queue.Add() // TODO we should, for consistency, just put this inside Queue.Add()
h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript}) h.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})
// OK! // OK!
RenderText(w, http.StatusText(http.StatusOK), http.StatusOK) RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)

View file

@ -9,7 +9,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/drone/drone/pkg/build/script"
"github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database"
. "github.com/drone/drone/pkg/model" . "github.com/drone/drone/pkg/model"
"github.com/drone/drone/pkg/queue" "github.com/drone/drone/pkg/queue"
@ -207,7 +206,7 @@ func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
// get the drone.yml file from GitHub // get the drone.yml file from GitHub
client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken) client := gogitlab.NewGitlab(settings.GitlabApiUrl, g.apiPath, user.GitlabToken)
content, err := client.RepoRawFile(ns(repo.Owner, repo.Name), commit.Hash, ".drone.yml") buildscript, err := client.RepoRawFile(ns(repo.Owner, repo.Name), commit.Hash, ".drone.yml")
if err != nil { if err != nil {
msg := "No .drone.yml was found in this repository. You need to add one.\n" msg := "No .drone.yml was found in this repository. You need to add one.\n"
if err := saveFailedBuild(commit, msg); err != nil { if err := saveFailedBuild(commit, msg); err != nil {
@ -216,16 +215,6 @@ func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
} }
// parse the build script
buildscript, err := script.ParseBuild(content, repo.Params)
if err != nil {
msg := "Could not parse your .drone.yml file. It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
if err := saveFailedBuild(commit, msg); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return RenderText(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
}
// save the commit to the database // save the commit to the database
if err := database.SaveCommit(commit); err != nil { if err := database.SaveCommit(commit); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
@ -237,11 +226,12 @@ func (g *GitlabHandler) Hook(w http.ResponseWriter, r *http.Request) error {
build.CommitID = commit.ID build.CommitID = commit.ID
build.Created = time.Now().UTC() build.Created = time.Now().UTC()
build.Status = "Pending" build.Status = "Pending"
build.BuildScript = string(buildscript)
if err := database.SaveBuild(build); err != nil { if err := database.SaveBuild(build); err != nil {
return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return RenderText(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
} }
g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript}) g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})
// OK! // OK!
return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK) return RenderText(w, http.StatusText(http.StatusOK), http.StatusOK)
@ -287,7 +277,7 @@ func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, use
commit.Timestamp = src.Commit.AuthoredDateRaw commit.Timestamp = src.Commit.AuthoredDateRaw
commit.SetAuthor(src.Commit.Author.Email) commit.SetAuthor(src.Commit.Author.Email)
content, err := client.RepoRawFile(strconv.Itoa(obj.SourceProjectId), commit.Hash, ".drone.yml") buildscript, err := client.RepoRawFile(strconv.Itoa(obj.SourceProjectId), commit.Hash, ".drone.yml")
if err != nil { if err != nil {
msg := "No .drone.yml was found in this repository. You need to add one.\n" msg := "No .drone.yml was found in this repository. You need to add one.\n"
if err := saveFailedBuild(commit, msg); err != nil { if err := saveFailedBuild(commit, msg); err != nil {
@ -296,16 +286,6 @@ func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, use
return fmt.Errorf("Error to fetch build script: %q", err) return fmt.Errorf("Error to fetch build script: %q", err)
} }
// parse the build script
buildscript, err := script.ParseBuild(content, repo.Params)
if err != nil {
msg := "Could not parse your .drone.yml file. It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n"
if err := saveFailedBuild(commit, msg); err != nil {
return fmt.Errorf("Failed to save build: %q", err)
}
return fmt.Errorf("Failed to parse build script: %q", err)
}
// save the commit to the database // save the commit to the database
if err := database.SaveCommit(commit); err != nil { if err := database.SaveCommit(commit); err != nil {
return fmt.Errorf("Failed to save commit: %q", err) return fmt.Errorf("Failed to save commit: %q", err)
@ -317,11 +297,12 @@ func (g *GitlabHandler) PullRequestHook(p *gogitlab.HookPayload, repo *Repo, use
build.CommitID = commit.ID build.CommitID = commit.ID
build.Created = time.Now().UTC() build.Created = time.Now().UTC()
build.Status = "Pending" build.Status = "Pending"
build.BuildScript = string(buildscript)
if err := database.SaveBuild(build); err != nil { if err := database.SaveBuild(build); err != nil {
return fmt.Errorf("Failed to save build: %q", err) return fmt.Errorf("Failed to save build: %q", err)
} }
g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build, Script: buildscript}) g.queue.Add(&queue.BuildTask{Repo: repo, Commit: commit, Build: build})
return nil return nil
} }

View file

@ -142,11 +142,9 @@ func (h RepoAdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// The User must own the repository OR be a member // The User must own the repository OR be a member
// of the Team that owns the repository. // of the Team that owns the repository.
if user.ID != repo.UserID { if admin, _ := database.IsRepoAdmin(user, repo); admin == false {
if admin, _ := database.IsMemberAdmin(user.ID, repo.TeamID); admin == false { RenderNotFound(w)
RenderNotFound(w) return
return
}
} }
if err = h(w, r, user, repo); err != nil { if err = h(w, r, user, repo); err != nil {

View file

@ -45,7 +45,7 @@ func TestRepoHandler(t *testing.T) {
So(rec.Code, ShouldEqual, 303) So(rec.Code, ShouldEqual, 303)
}) })
Convey("Private repo can not be viewed by a non team member", func() { Convey("Private repo can not be viewed by a non team member", func() {
req, err := http.NewRequest("GET", "/github.com/drone/drone", nil) req, err := http.NewRequest("GET", "/github.com/drone/drone", nil)
So(err, ShouldBeNil) So(err, ShouldBeNil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
setUserSession(rec, req, "rick@el.to.ro") setUserSession(rec, req, "rick@el.to.ro")
@ -61,7 +61,7 @@ func dummyUserRepo(w http.ResponseWriter, r *http.Request, u *User, repo *Repo)
func setUserSession(w http.ResponseWriter, r *http.Request, username string) { func setUserSession(w http.ResponseWriter, r *http.Request, username string) {
handler.SetCookie(w, r, "_sess", username) handler.SetCookie(w, r, "_sess", username)
resp := http.Response{Header: w.Header()} resp := http.Response{Header: w.Header()}
for _, v := range resp.Cookies() { for _, v := range resp.Cookies() {
r.AddCookie(v) r.AddCookie(v)
} }

View file

@ -15,16 +15,17 @@ const (
) )
type Build struct { type Build struct {
ID int64 `meddler:"id,pk" json:"id"` ID int64 `meddler:"id,pk" json:"id"`
CommitID int64 `meddler:"commit_id" json:"-"` CommitID int64 `meddler:"commit_id" json:"-"`
Slug string `meddler:"slug" json:"slug"` Slug string `meddler:"slug" json:"slug"`
Status string `meddler:"status" json:"status"` Status string `meddler:"status" json:"status"`
Started time.Time `meddler:"started,utctime" json:"started"` Started time.Time `meddler:"started,utctime" json:"started"`
Finished time.Time `meddler:"finished,utctime" json:"finished"` Finished time.Time `meddler:"finished,utctime" json:"finished"`
Duration int64 `meddler:"duration" json:"duration"` Duration int64 `meddler:"duration" json:"duration"`
Created time.Time `meddler:"created,utctime" json:"created"` Created time.Time `meddler:"created,utctime" json:"created"`
Updated time.Time `meddler:"updated,utctime" json:"updated"` Updated time.Time `meddler:"updated,utctime" json:"updated"`
Stdout string `meddler:"stdout" json:"-"` Stdout string `meddler:"stdout" json:"-"`
BuildScript string `meddler:"buildscript" json:"-"`
} }
// HumanDuration returns a human-readable approximation of a duration // HumanDuration returns a human-readable approximation of a duration

View file

@ -2,6 +2,7 @@ package publish
import ( import (
"fmt" "fmt"
"strings"
"github.com/drone/drone/pkg/build/buildfile" "github.com/drone/drone/pkg/build/buildfile"
) )
@ -37,12 +38,18 @@ type Swift struct {
} }
func (s *Swift) Write(f *buildfile.Buildfile) { func (s *Swift) Write(f *buildfile.Buildfile) {
var target string
// All options are required, so ensure they are present // All options are required, so ensure they are present
if len(s.Username) == 0 || len(s.Password) == 0 || len(s.AuthURL) == 0 || len(s.Region) == 0 || len(s.Source) == 0 || len(s.Container) == 0 || len(s.Target) == 0 { if len(s.Username) == 0 || len(s.Password) == 0 || len(s.AuthURL) == 0 || len(s.Region) == 0 || len(s.Source) == 0 || len(s.Container) == 0 {
f.WriteCmdSilent(`echo "Swift: Missing argument(s)"`) f.WriteCmdSilent(`echo "Swift: Missing argument(s)"`)
return return
} }
// If a target was provided, prefix it with a /
if len(s.Target) > 0 {
target = fmt.Sprintf("/%s", strings.TrimPrefix(s.Target, "/"))
}
// debugging purposes so we can see if / where something is failing // debugging purposes so we can see if / where something is failing
f.WriteCmdSilent(`echo "Swift: Publishing..."`) f.WriteCmdSilent(`echo "Swift: Publishing..."`)
@ -56,5 +63,5 @@ func (s *Swift) Write(f *buildfile.Buildfile) {
f.WriteEnv("SWIFTLY_AUTH_KEY", s.Password) f.WriteEnv("SWIFTLY_AUTH_KEY", s.Password)
f.WriteEnv("SWIFTLY_REGION", s.Region) f.WriteEnv("SWIFTLY_REGION", s.Region)
f.WriteCmd(fmt.Sprintf(`swiftly put -i %s %s/%s`, s.Source, s.Container, s.Target)) f.WriteCmd(fmt.Sprintf(`swiftly put -i %s %s%s`, s.Source, s.Container, target))
} }

View file

@ -1,7 +1,6 @@
package queue package queue
import ( import (
"github.com/drone/drone/pkg/build/script"
. "github.com/drone/drone/pkg/model" . "github.com/drone/drone/pkg/model"
) )
@ -16,10 +15,6 @@ type BuildTask struct {
Repo *Repo Repo *Repo
Commit *Commit Commit *Commit
Build *Build Build *Build
// Build instructions from the .drone.yml
// file, unmarshalled.
Script *script.Build
} }
// Start N workers with the given build runner. // Start N workers with the given build runner.

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/drone/drone/pkg/build/git" "github.com/drone/drone/pkg/build/git"
r "github.com/drone/drone/pkg/build/repo" r "github.com/drone/drone/pkg/build/repo"
"github.com/drone/drone/pkg/build/script"
"github.com/drone/drone/pkg/channel" "github.com/drone/drone/pkg/channel"
"github.com/drone/drone/pkg/database" "github.com/drone/drone/pkg/database"
. "github.com/drone/drone/pkg/model" . "github.com/drone/drone/pkg/model"
@ -83,9 +84,16 @@ func (w *worker) execute(task *BuildTask) error {
Host: settings.URL().String(), Host: settings.URL().String(),
} }
// parse the build script
buildscript, err := script.ParseBuild([]byte(task.Build.BuildScript), task.Repo.Params)
if err != nil {
log.Printf("Could not parse your .drone.yml file. It needs to be a valid drone yaml file.\n\n" + err.Error() + "\n")
return err
}
// send all "started" notifications // send all "started" notifications
if task.Script.Notifications != nil { if buildscript.Notifications != nil {
task.Script.Notifications.Send(context) buildscript.Notifications.Send(context)
} }
// Send "started" notification to Github // Send "started" notification to Github
@ -113,7 +121,7 @@ func (w *worker) execute(task *BuildTask) error {
// this is not a pull request (for security purposes) // this is not a pull request (for security purposes)
if task.Repo.Params != nil && len(task.Commit.PullRequest) == 0 { if task.Repo.Params != nil && len(task.Commit.PullRequest) == 0 {
for k, v := range task.Repo.Params { for k, v := range task.Repo.Params {
task.Script.Env = append(task.Script.Env, k+"="+v) buildscript.Env = append(buildscript.Env, k+"="+v)
} }
} }
@ -126,7 +134,7 @@ func (w *worker) execute(task *BuildTask) error {
}() }()
// execute the build // execute the build
passed, buildErr := w.runBuild(task, buf) passed, buildErr := w.runBuild(task, buildscript, buf)
task.Build.Finished = time.Now().UTC() task.Build.Finished = time.Now().UTC()
task.Commit.Finished = time.Now().UTC() task.Commit.Finished = time.Now().UTC()
@ -162,14 +170,14 @@ func (w *worker) execute(task *BuildTask) error {
channel.Close(consoleslug) channel.Close(consoleslug)
// send all "finished" notifications // send all "finished" notifications
if task.Script.Notifications != nil { if buildscript.Notifications != nil {
task.Script.Notifications.Send(context) buildscript.Notifications.Send(context)
} }
return nil return nil
} }
func (w *worker) runBuild(task *BuildTask, buf io.Writer) (bool, error) { func (w *worker) runBuild(task *BuildTask, buildscript *script.Build, buf io.Writer) (bool, error) {
repo := &r.Repo{ repo := &r.Repo{
Name: task.Repo.Slug, Name: task.Repo.Slug,
Path: task.Repo.URL, Path: task.Repo.URL,
@ -177,11 +185,11 @@ func (w *worker) runBuild(task *BuildTask, buf io.Writer) (bool, error) {
Commit: task.Commit.Hash, Commit: task.Commit.Hash,
PR: task.Commit.PullRequest, PR: task.Commit.PullRequest,
Dir: filepath.Join("/var/cache/drone/src", task.Repo.Slug), Dir: filepath.Join("/var/cache/drone/src", task.Repo.Slug),
Depth: git.GitDepth(task.Script.Git), Depth: git.GitDepth(buildscript.Git),
} }
return w.runner.Run( return w.runner.Run(
task.Script, buildscript,
repo, repo,
[]byte(task.Repo.PrivateKey), []byte(task.Repo.PrivateKey),
task.Repo.Privileged, task.Repo.Privileged,

View file

@ -24,6 +24,18 @@
{{ else }} {{ else }}
<span>commit <span>{{ .Commit.HashShort }}</span> to <span>{{.Commit.Branch}}</span> branch</span> <span>commit <span>{{ .Commit.HashShort }}</span> to <span>{{.Commit.Branch}}</span> branch</span>
{{ end }} {{ end }}
<div class="actions">
{{ if .IsAdmin }}
{{ if not .Build.IsRunning }}
<form action="/{{.Repo.Slug}}/commit/{{.Commit.Hash}}/rebuild?branch={{ .Commit.Branch }}"
method="POST">
<input class="btn btn-default" type="submit" value="Rebuild"/>
</form>
{{ end }}
{{ end }}
</div>
</div> </div>
<div class="build-details container affix-top" data-spy="affix" data-offset-top="248"> <div class="build-details container affix-top" data-spy="affix" data-offset-top="248">
<div class="build-summary"> <div class="build-summary">