Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
d14ae26a64
25 changed files with 440 additions and 229 deletions
|
@ -90,12 +90,8 @@ func (c *Client) run(method, path string, in, out interface{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Read the bytes from the body (make sure we defer close the body)
|
||||
// make sure we defer close the body
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check for an http error status (ie not 200 StatusOK)
|
||||
switch resp.StatusCode {
|
||||
|
@ -111,9 +107,9 @@ func (c *Client) run(method, path string, in, out interface{}) error {
|
|||
return ErrInternalServer
|
||||
}
|
||||
|
||||
// Unmarshall the JSON response
|
||||
// Decode the JSON response
|
||||
if out != nil {
|
||||
return json.Unmarshal(body, out)
|
||||
return json.NewDecoder(resp.Body).Decode(out)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -81,7 +81,7 @@ func (s *SSH) Write(f *buildfile.Buildfile) {
|
|||
}
|
||||
|
||||
if len(s.Cmd) > 0 {
|
||||
sshCmd := "ssh -o StrictHostKeyChecking=no -p %s %s %s"
|
||||
sshCmd := "ssh -o StrictHostKeyChecking=no -p %s %s \"%s\""
|
||||
f.WriteCmd(fmt.Sprintf(sshCmd, host[1], strings.SplitN(host[0], ":", 2)[0], s.Cmd))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ func TestSSHNoArtifact(t *testing.T) {
|
|||
t.Error("Expect script not to contains scp command")
|
||||
}
|
||||
|
||||
if !strings.Contains(bscr, "ssh -o StrictHostKeyChecking=no -p 22 user@test.example.com /opt/bin/redeploy.sh") {
|
||||
if !strings.Contains(bscr, "ssh -o StrictHostKeyChecking=no -p 22 user@test.example.com \"/opt/bin/redeploy.sh\"") {
|
||||
t.Error("Expect script to contains ssh command")
|
||||
}
|
||||
}
|
||||
|
|
28
plugin/notify/notify_test.go
Normal file
28
plugin/notify/notify_test.go
Normal file
|
@ -0,0 +1,28 @@
|
|||
package notify
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/shared/model"
|
||||
)
|
||||
|
||||
func Test_getBuildUrl(t *testing.T) {
|
||||
c := &model.Request{
|
||||
Host: "http://examplehost.com",
|
||||
Repo: &model.Repo{
|
||||
Host: "examplegit.com",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
},
|
||||
Commit: &model.Commit{
|
||||
Sha: "abc",
|
||||
Branch: "example",
|
||||
},
|
||||
}
|
||||
expected := "http://examplehost.com/examplegit.com/owner/repo/example/abc"
|
||||
output := getBuildUrl(c)
|
||||
|
||||
if output != expected {
|
||||
t.Errorf("Failed to build url. Expected: %s, got %s", expected, output)
|
||||
}
|
||||
}
|
|
@ -9,9 +9,9 @@ import (
|
|||
|
||||
const (
|
||||
slackEndpoint = "https://%s.slack.com/services/hooks/incoming-webhook?token=%s"
|
||||
slackStartedMessage = "*Building* %s, commit <%s|%s>, author %s"
|
||||
slackSuccessMessage = "*Success* %s, commit <%s|%s>, author %s"
|
||||
slackFailureMessage = "*Failed* %s, commit <%s|%s>, author %s"
|
||||
slackStartedMessage = "*Building* <%s|%s> (%s) by %s"
|
||||
slackSuccessMessage = "*Success* <%s|%s> (%s) by %s"
|
||||
slackFailureMessage = "*Failed* <%s|%s> (%s) by %s"
|
||||
)
|
||||
|
||||
type Slack struct {
|
||||
|
@ -39,11 +39,14 @@ func (s *Slack) Send(context *model.Request) error {
|
|||
|
||||
func (s *Slack) getMessage(context *model.Request, message string) string {
|
||||
url := getBuildUrl(context)
|
||||
return fmt.Sprintf(message, context.Repo.Name, url, context.Commit.ShaShort(), context.Commit.Author)
|
||||
// drone/drone#3333333
|
||||
linktext := context.Repo.Owner + "/" + context.Repo.Name + "#" + context.Commit.ShaShort()
|
||||
|
||||
return fmt.Sprintf(message, linktext, url, context.Commit.Branch, context.Commit.Author)
|
||||
}
|
||||
|
||||
func (s *Slack) sendStarted(context *model.Request) error {
|
||||
return s.send(s.getMessage(context, slackStartedMessage), "warning")
|
||||
return s.send(s.getMessage(context, slackStartedMessage)+"\n - "+context.Commit.Message, "warning")
|
||||
}
|
||||
|
||||
func (s *Slack) sendSuccess(context *model.Request) error {
|
||||
|
|
|
@ -1,27 +1,57 @@
|
|||
package notify
|
||||
|
||||
import (
|
||||
"github.com/drone/drone/shared/model"
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func Test_getBuildUrl(t *testing.T) {
|
||||
c := &model.Request{
|
||||
Host: "http://examplehost.com",
|
||||
Repo: &model.Repo{
|
||||
Host: "examplegit.com",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
},
|
||||
Commit: &model.Commit{
|
||||
Sha: "abc",
|
||||
Branch: "example",
|
||||
},
|
||||
}
|
||||
expected := "http://examplehost.com/examplegit.com/owner/repo/example/abc"
|
||||
output := getBuildUrl(c)
|
||||
/*
|
||||
var request = &model.Request{
|
||||
Host: "http://examplehost.com",
|
||||
Repo: &model.Repo{
|
||||
Host: "examplegit.com",
|
||||
Owner: "owner",
|
||||
Name: "repo",
|
||||
},
|
||||
Commit: &model.Commit{
|
||||
Sha: "abc",
|
||||
Branch: "example",
|
||||
Status: "Started",
|
||||
Message: "Test Commit",
|
||||
Author: "Test User",
|
||||
},
|
||||
User: &model.User{
|
||||
Login: "TestUser",
|
||||
},
|
||||
}
|
||||
*/
|
||||
|
||||
if output != expected {
|
||||
t.Errorf("Failed to build url. Expected: %s, got %s", expected, output)
|
||||
var slackExpectedLink = "<owner/repo#abc|http://examplehost.com/examplegit.com/owner/repo/example/abc>"
|
||||
var slackExpectedBase = slackExpectedLink + " (example) by Test User"
|
||||
|
||||
func Test_slackStartedMessage(t *testing.T) {
|
||||
actual := (&Slack{}).getMessage(request, slackStartedMessage)
|
||||
|
||||
expected := "*Building* " + slackExpectedBase
|
||||
|
||||
if actual != expected {
|
||||
t.Errorf("Invalid getStarted message for Slack. Expected %v, got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_slackSuccessMessage(t *testing.T) {
|
||||
actual := (&Slack{}).getMessage(request, slackSuccessMessage)
|
||||
|
||||
expected := "*Success* " + slackExpectedBase
|
||||
|
||||
if actual != expected {
|
||||
t.Errorf("Invalid getStarted message for Slack. Expected %v, got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_slackFailureMessage(t *testing.T) {
|
||||
actual := (&Slack{}).getMessage(request, slackFailureMessage)
|
||||
|
||||
expected := "*Failed* " + slackExpectedBase
|
||||
|
||||
if actual != expected {
|
||||
t.Errorf("Invalid getStarted message for Slack. Expected %v, got %v", expected, actual)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ func (r *Gitlab) GetRepos(user *model.User) ([]*model.Repo, error) {
|
|||
CloneURL: item.HttpRepoUrl,
|
||||
GitURL: item.HttpRepoUrl,
|
||||
SSHURL: item.SshRepoUrl,
|
||||
URL: item.Url,
|
||||
Role: &model.Perm{},
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
<script src="/static/scripts/controllers/conf.js"></script>
|
||||
<script src="/static/scripts/controllers/home.js"></script>
|
||||
<script src="/static/scripts/controllers/repo.js"></script>
|
||||
<script src="/static/scripts/controllers/commit.js"></script>
|
||||
<script src="/static/scripts/controllers/user.js"></script>
|
||||
<script src="/static/scripts/controllers/users.js"></script>
|
||||
<script src="/static/scripts/controllers/setup.js"></script>
|
||||
|
|
|
@ -198,10 +198,23 @@ app.run(['$location', '$rootScope', '$routeParams', 'feed', 'stdout', function($
|
|||
|
||||
|
||||
|
||||
app.controller("AccountReposController", function($scope, $http, user) {
|
||||
app.controller("AccountReposController", function($scope, $http, $location, user) {
|
||||
|
||||
$scope.user = user;
|
||||
|
||||
$scope.syncUser = function() {
|
||||
$http({method: 'POST', url: '/api/user/sync' }).success(function(data){
|
||||
$location.search('return_to', $location.$$path).path('/sync')
|
||||
}).error(function(data, status){
|
||||
if (status == 409) {
|
||||
$scope.msg = 'already'
|
||||
} else {
|
||||
$scope.msg = 'bad'
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
// get the user details
|
||||
$http({method: 'GET', url: '/api/user/repos'}).
|
||||
success(function(data, status, headers, config) {
|
||||
|
@ -224,93 +237,3 @@ app.controller("AccountReposController", function($scope, $http, user) {
|
|||
return $scope.remote == "" || $scope.remote == entry.remote;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
app.controller("CommitController", function($scope, $http, $route, $routeParams, stdout, feed) {
|
||||
|
||||
var remote = $routeParams.remote;
|
||||
var owner = $routeParams.owner;
|
||||
var name = $routeParams.name;
|
||||
var branch = $routeParams.branch;
|
||||
var commit = $routeParams.commit;
|
||||
$scope.console='';
|
||||
|
||||
var handleOutput = function(id, clearConsole) {
|
||||
var lineFormatter = new Drone.LineFormatter();
|
||||
var el = document.querySelector('#output');
|
||||
if(clearConsole === true) {
|
||||
el.innerHTML = '';
|
||||
}
|
||||
stdout.subscribe(id, function(out){
|
||||
angular.element(el).append(lineFormatter.format(out));
|
||||
if ($scope.following) {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
feed.subscribe(function(item) {
|
||||
if (item.commit.sha == commit &&
|
||||
item.commit.branch == branch) {
|
||||
if(item.commit.status == "Started") {
|
||||
handleOutput(item.commit.id, true);
|
||||
}
|
||||
$scope.commit = item.commit;
|
||||
$scope.$apply();
|
||||
|
||||
} else {
|
||||
// we trigger an toast notification so the
|
||||
// user is aware another build started
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// load the repo meta-data
|
||||
$http({method: 'GET', url: '/api/repos/'+remote+'/'+owner+"/"+name}).
|
||||
success(function(data, status, headers, config) {
|
||||
$scope.repo = data;
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
// load the repo commit data
|
||||
$http({method: 'GET', url: '/api/repos/'+remote+'/'+owner+"/"+name+"/branches/"+branch+"/commits/"+commit}).
|
||||
success(function(data, status, headers, config) {
|
||||
$scope.commit = data;
|
||||
|
||||
if (data.status!='Started' && data.status!='Pending') {
|
||||
$http({method: 'GET', url: '/api/repos/'+remote+'/'+owner+"/"+name+"/branches/"+branch+"/commits/"+commit+"/console"}).
|
||||
success(function(data, status, headers, config) {
|
||||
var lineFormatter = new Drone.LineFormatter();
|
||||
var el = document.querySelector('#output');
|
||||
angular.element(el).append(lineFormatter.format(data));
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
console.log(data);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
handleOutput(data.id, false);
|
||||
|
||||
}).
|
||||
error(function(data, status, headers, config) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
$scope.following = false;
|
||||
$scope.follow = function() {
|
||||
$scope.following = true;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
$scope.unfollow = function() {
|
||||
$scope.following = false;
|
||||
}
|
||||
|
||||
$scope.rebuildCommit = function() {
|
||||
$http({method: 'POST', url: '/api/repos/'+remote+'/'+owner+'/'+name+'/'+'branches/'+branch+'/'+'commits/'+commit+'?action=rebuild' });
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
|
95
server/app/scripts/controllers/commit.js
Normal file
95
server/app/scripts/controllers/commit.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*global angular, Drone, console */
|
||||
angular.module('app').controller("CommitController", function ($scope, $http, $route, $routeParams, stdout, feed) {
|
||||
'use strict';
|
||||
|
||||
var remote = $routeParams.remote,
|
||||
owner = $routeParams.owner,
|
||||
name = $routeParams.name,
|
||||
branch = $routeParams.branch,
|
||||
commit = $routeParams.commit,
|
||||
// Create lineFormatter and outputElement since we need them anyway.
|
||||
lineFormatter = new Drone.LineFormatter(),
|
||||
outputElement = angular.element(document.querySelector('#output'));
|
||||
|
||||
var connectRemoteConsole = function (id) {
|
||||
// Clear console output if connecting to new remote console (rebuild)
|
||||
if (!outputElement.html() !== 0) {
|
||||
outputElement.empty();
|
||||
}
|
||||
// Subscribe to stdout of the remote build
|
||||
stdout.subscribe(id, function (out) {
|
||||
// Append new output to console
|
||||
outputElement.append(lineFormatter.format(out));
|
||||
// Scroll if following
|
||||
if ($scope.following) {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Subscribe to feed so we can update gui if changes to the commit happen. (Build finished, Rebuild triggered, change from Pending to Started)
|
||||
feed.subscribe(function (item) {
|
||||
// If event is part of the active commit currently showing.
|
||||
if (item.commit.sha === commit &&
|
||||
item.commit.branch === branch) {
|
||||
// If new status is Started, connect to remote console to get live output
|
||||
if (item.commit.status === "Started") {
|
||||
connectRemoteConsole(item.commit.id);
|
||||
}
|
||||
$scope.commit = item.commit;
|
||||
$scope.$apply();
|
||||
|
||||
} else {
|
||||
// we trigger an toast notification so the
|
||||
// user is aware another build started
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Load the repo meta-data
|
||||
$http({method: 'GET', url: '/api/repos/' + remote + '/' + owner + "/" + name}).
|
||||
success(function (data, status, headers, config) {
|
||||
$scope.repo = data;
|
||||
}).
|
||||
error(function (data, status, headers, config) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
// Load the repo commit data
|
||||
$http({method: 'GET', url: '/api/repos/' + remote + '/' + owner + "/" + name + "/branches/" + branch + "/commits/" + commit}).
|
||||
success(function (data, status, headers, config) {
|
||||
$scope.commit = data;
|
||||
|
||||
// If build has already finished, load console output from database
|
||||
if (data.status !== 'Started' && data.status !== 'Pending') {
|
||||
$http({method: 'GET', url: '/api/repos/' + remote + '/' + owner + "/" + name + "/branches/" + branch + "/commits/" + commit + "/console"}).
|
||||
success(function (data, status, headers, config) {
|
||||
outputElement.append(lineFormatter.format(data));
|
||||
}).
|
||||
error(function (data, status, headers, config) {
|
||||
console.log(data);
|
||||
});
|
||||
return;
|
||||
// If build is currently running, connect to remote console;
|
||||
} else if (data.status === 'Started') {
|
||||
connectRemoteConsole(data.id);
|
||||
}
|
||||
|
||||
}).
|
||||
error(function (data, status, headers, config) {
|
||||
console.log(data);
|
||||
});
|
||||
|
||||
$scope.following = false;
|
||||
$scope.follow = function () {
|
||||
$scope.following = true;
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
};
|
||||
$scope.unfollow = function () {
|
||||
$scope.following = false;
|
||||
};
|
||||
|
||||
$scope.rebuildCommit = function () {
|
||||
$http({method: 'POST', url: '/api/repos/' + remote + '/' + owner + '/' + name + '/branches/' + branch + '/commits/' + commit + '?action=rebuild' });
|
||||
};
|
||||
});
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('app').controller("HomeController", function($scope, $http, feed) {
|
||||
angular.module('app').controller("HomeController", function($scope, $http, $location, feed) {
|
||||
|
||||
feed.subscribe(function(item) {
|
||||
// todo toast notification
|
||||
|
@ -14,6 +14,19 @@ angular.module('app').controller("HomeController", function($scope, $http, feed)
|
|||
console.log(data);
|
||||
});
|
||||
|
||||
$scope.syncUser = function() {
|
||||
$http({method: 'POST', url: '/api/user/sync' }).success(function(data){
|
||||
$location.search('return_to', $location.$$path).path('/sync')
|
||||
}).error(function(data, status){
|
||||
if (status == 409) {
|
||||
$scope.msg = 'already'
|
||||
} else {
|
||||
$scope.msg = 'bad'
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
}
|
||||
|
||||
$http({method: 'GET', url: '/api/user/repos'}).
|
||||
success(function(data, status, headers, config) {
|
||||
$scope.repos = (typeof data==='string')?[]:data;
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
'use strict';
|
||||
|
||||
angular.module('app').controller("SyncController", function($scope, $http, $interval, $location, users) {
|
||||
angular.module('app').controller("SyncController", function($scope, $http, $interval, $location, $routeParams, users) {
|
||||
var return_to = $routeParams.return_to
|
||||
var stop = $interval(function() {
|
||||
// todo(bradrydzewski) We should poll the user to see if the
|
||||
// sync process is complete, using the user.syncing variable.
|
||||
$interval.cancel(stop);
|
||||
$location.path("/");
|
||||
if (return_to != undefined) {
|
||||
$location.$$search = {}
|
||||
$location.path(return_to);
|
||||
} else {
|
||||
$location.path("/");
|
||||
}
|
||||
}, 5000);
|
||||
});
|
||||
|
|
|
@ -1,32 +1,34 @@
|
|||
'use strict';
|
||||
/*global angular, WebSocket, localStorage, console */
|
||||
angular.module('app').service('stdout', ['$window', function ($window) {
|
||||
'use strict';
|
||||
|
||||
var callback,
|
||||
websocket,
|
||||
token = localStorage.getItem('access_token');
|
||||
|
||||
angular.module('app').service('stdout', ['$window', function($window) {
|
||||
var callback = undefined;
|
||||
var websocket = undefined;
|
||||
var token = localStorage.getItem('access_token');
|
||||
|
||||
this.subscribe = function(path, _callback) {
|
||||
this.subscribe = function (path, _callback) {
|
||||
callback = _callback;
|
||||
|
||||
var proto = ($window.location.protocol == 'https:' ? 'wss' : 'ws');
|
||||
var route = [proto, "://", $window.location.host, '/api/stream/stdout/', path, '?access_token=', token].join('');
|
||||
var proto = ($window.location.protocol === 'https:' ? 'wss' : 'ws'),
|
||||
route = [proto, "://", $window.location.host, '/api/stream/stdout/', path, '?access_token=', token].join('');
|
||||
|
||||
websocket = new WebSocket(route);
|
||||
websocket.onmessage = function(event) {
|
||||
if (callback != undefined) {
|
||||
websocket.onmessage = function (event) {
|
||||
if (callback !== undefined) {
|
||||
callback(event.data);
|
||||
}
|
||||
};
|
||||
websocket.onclose = function(event) {
|
||||
console.log('websocket closed at '+path);
|
||||
websocket.onclose = function (event) {
|
||||
console.log('websocket closed at ' + path);
|
||||
};
|
||||
};
|
||||
|
||||
this.unsubscribe = function() {
|
||||
this.unsubscribe = function () {
|
||||
callback = undefined;
|
||||
if (websocket != undefined) {
|
||||
console.log('unsubscribing websocket at '+websocket.url);
|
||||
if (websocket !== undefined) {
|
||||
console.log('unsubscribing websocket at ' + websocket.url);
|
||||
websocket.close();
|
||||
websocket = undefined;
|
||||
}
|
||||
};
|
||||
}]);
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1229,6 +1229,11 @@ nav {
|
|||
//border-left:1px solid #cdcece;
|
||||
//box-shadow:-3px 0 0 rgba(0,0,0,.05);
|
||||
|
||||
#sidebar-inner {
|
||||
position: fixed;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
h1 {font-size:28px; font-weight:300;}
|
||||
h2 {font-size:22px; font-weight:300; margin-bottom:20px;}
|
||||
dl {padding-top:23px; border-top:1px solid #ddd; margin-top:5px;
|
||||
|
|
|
@ -1,37 +1,22 @@
|
|||
<aside id="sidebar">
|
||||
<div class="result" data-result="{{ commit.status }}">
|
||||
<dl>
|
||||
<dd><span class="status">{{ commit.status }}</span></dd>
|
||||
<dd><strong>{{ commit.message }}</strong></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<dl ng-if="commit.duration != 0">
|
||||
<dd><h1>{{ commit.duration | toDuration}}</h1></dd>
|
||||
</dl>
|
||||
<div id="sidebar-inner">
|
||||
<div class="result" data-result="{{ commit.status }}">
|
||||
<dl>
|
||||
<dd><span class="status">{{ commit.status }}</span></dd>
|
||||
<dd><strong>{{ commit.message }}</strong></dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<dl>
|
||||
<dd class="large" ng-if="repo.remote == 'gitlab.com' || repo.remote == 'github.com' || repo.remote == 'enterprise.github.com' ">
|
||||
<strong>
|
||||
commit
|
||||
<a href="http://{{ repo.host }}/{{ repo.owner }}/{{ repo.name }}/commit/{{ commit.sha }}" >{{ commit.sha | shortHash}}</a>
|
||||
to <a href="http://{{ repo.host }}/{{ repo.owner }}/{{ repo.name }}/tree/{{ commit.branch }}">{{ commit.branch }}</a> branch
|
||||
</strong>
|
||||
</dd>
|
||||
<dd class="large" ng-if="repo.remote == 'bitbucket.org' ">
|
||||
<strong>
|
||||
commit
|
||||
<a href="http://{{ repo.host }}/{{ repo.owner }}/{{ repo.name }}/commits/{{ commit.sha }}" >{{ commit.sha | shortHash}}</a>
|
||||
to <a href="http://{{ repo.host }}/{{ repo.owner }}/{{ repo.name }}/src/?at={{ commit.branch }}">{{ commit.branch }}</a> branch
|
||||
</strong>
|
||||
</dd>
|
||||
<dd class="large" ng-if="repo.remote != 'gitlab.com' && repo.remote != 'github.com' && repo.remote != 'enterprise.github.com' && repo.remote != 'bitbucket.org' ">
|
||||
<strong>commit <u>{{ commit.sha | shortHash}}</u> to <u>{{ commit.branch }}</u> branch</strong>
|
||||
</dd>
|
||||
<dl ng-if="commit.duration != 0">
|
||||
<dd><h1>{{ commit.duration | toDuration}}</h1></dd>
|
||||
</dl>
|
||||
|
||||
<dl ng-include="'/static/views/commit_detail.html'" ng-show="commit.pull_request.length == 0"></dl>
|
||||
<dl ng-include="'/static/views/commit_detail_pr.html'" ng-show="commit.pull_request.length != 0"></dl>
|
||||
<dd ng-if="commit.finished_at != 0">{{ commit.finished_at | fromNow }}</dd>
|
||||
<dd ng-if="commit.finished_at == 0 && commit.started_at != 0">Started {{ commit.started_at | fromNow }}</dd>
|
||||
<dd ng-if="commit.finished_at == 0 && commit.started_at == 0">Created {{ commit.created_at}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div id="main" class="output">
|
||||
|
|
23
server/app/views/commit_detail.html
Normal file
23
server/app/views/commit_detail.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
<!-- GITHUB && GITLAB -->
|
||||
<dd class="large" ng-if="repo.remote == 'gitlab.com' || repo.remote == 'github.com' || repo.remote == 'enterprise.github.com' ">
|
||||
<strong>
|
||||
commit
|
||||
<a href="{{ repo.url }}/commit/{{ commit.sha }}" >{{ commit.sha | shortHash}}</a>
|
||||
to <a href="{{ repo.url }}/tree/{{ commit.branch }}">{{ commit.branch }}</a> branch
|
||||
</strong>
|
||||
</dd>
|
||||
<!-- /GITHUB && GITLAB -->
|
||||
<!-- BITBUCKET -->
|
||||
<dd class="large" ng-if="repo.remote == 'bitbucket.org' ">
|
||||
<strong>
|
||||
commit
|
||||
<a href="{{ repo.url }}/commits/{{ commit.sha }}" >{{ commit.sha | shortHash}}</a>
|
||||
to <a href="{{ repo.url }}/src/?at={{ commit.branch }}">{{ commit.branch }}</a> branch
|
||||
</strong>
|
||||
</dd>
|
||||
<!-- /BITBUCKET -->
|
||||
<!-- STASH -->
|
||||
<dd class="large" ng-if="repo.remote != 'gitlab.com' && repo.remote != 'github.com' && repo.remote != 'enterprise.github.com' && repo.remote != 'bitbucket.org' ">
|
||||
<strong>commit <u>{{ commit.sha | shortHash}}</u> to <u>{{ commit.branch }}</u> branch</strong>
|
||||
</dd>
|
||||
<!-- /STASH -->
|
31
server/app/views/commit_detail_pr.html
Normal file
31
server/app/views/commit_detail_pr.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
<!-- GITHUB -->
|
||||
<dd class="large" ng-if="repo.remote == 'github.com' || repo.remote == 'enterprise.github.com' ">
|
||||
<strong>
|
||||
Pull Request
|
||||
<a href="{{ repo.url }}/pull/{{ commit.pull_request }}" >#{{ commit.pull_request }}</a>
|
||||
</strong>
|
||||
</dd>
|
||||
<!-- /GITHUB -->
|
||||
<!-- GITLAB -->
|
||||
<dd class="large" ng-if="repo.remote == 'gitlab.com'">
|
||||
<strong>
|
||||
Pull Request
|
||||
<a href="{{ repo.url }}/merge_requests/{{ commit.pull_request }}" >#{{ commit.pull_request }}</a>
|
||||
</strong>
|
||||
</dd>
|
||||
<!-- /GITLAB -->
|
||||
<!-- BITBUCKET -->
|
||||
<dd class="large" ng-if="repo.remote == 'bitbucket.org' ">
|
||||
<strong>
|
||||
Pull Request
|
||||
<a href="{{ repo.url }}/pull-requests/{{ commit.pull_request }}" >#{{ commit.pull_request }}</a>
|
||||
</strong>
|
||||
</dd>
|
||||
<!-- /BITBUCKET -->
|
||||
<!-- STASH -->
|
||||
<dd class="large" ng-if="repo.remote != 'gitlab.com' && repo.remote != 'github.com' && repo.remote != 'enterprise.github.com' && repo.remote != 'bitbucket.org' ">
|
||||
<strong>
|
||||
Pull Request #{{ commit.pull_request }}
|
||||
</strong>
|
||||
</dd>
|
||||
<!-- /STASH -->
|
|
@ -1,7 +1,18 @@
|
|||
<div class="toast" ng-if="msg != undefined">
|
||||
<span ng-if="msg == 'already'">sync already runned</span>
|
||||
<span ng-if="msg == 'bad'">bad response</span>
|
||||
</div>
|
||||
|
||||
<article id="homepage">
|
||||
<nav>
|
||||
<a href="/"><span class="fa fa-th"></span></a>
|
||||
<a href="/">dashboard</a>
|
||||
<div class="options ng-scope">
|
||||
<a class="pure-button pure-button-primary" ng-click="syncUser()" href="#">
|
||||
<i class="fa fa-refresh"></i>
|
||||
<span>Sync</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section ng-if="feed.length == 0">
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
<div class="toast" ng-if="msg != undefined">
|
||||
<span ng-if="msg == 'already'">sync already runned</span>
|
||||
<span ng-if="msg == 'bad'">bad response</span>
|
||||
</div>
|
||||
|
||||
<div id="repospage">
|
||||
<nav>
|
||||
<a href="/"><span class="fa fa-th"></span></a>
|
||||
<a href="/">repositories</a>
|
||||
|
||||
<div class="options ng-scope">
|
||||
<a class="pure-button pure-button-primary" ng-click="syncUser()" href="#">
|
||||
<i class="fa fa-refresh"></i>
|
||||
<span>Sync</span>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section>
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/drone/drone/plugin/remote"
|
||||
"github.com/drone/drone/server/capability"
|
||||
"github.com/drone/drone/server/datastore"
|
||||
"github.com/drone/drone/server/session"
|
||||
"github.com/drone/drone/server/sync"
|
||||
"github.com/drone/drone/shared/model"
|
||||
"github.com/goji/context"
|
||||
"github.com/zenazn/goji/web"
|
||||
|
@ -83,7 +83,8 @@ func GetLogin(c web.C, w http.ResponseWriter, r *http.Request) {
|
|||
u.Secret = login.Secret
|
||||
u.Name = login.Name
|
||||
u.SetEmail(login.Email)
|
||||
u.Syncing = true //u.IsStale() // todo (badrydzewski) should not always sync
|
||||
u.Syncing = u.IsStale()
|
||||
|
||||
if err := datastore.PutUser(ctx, u); err != nil {
|
||||
log.Println(err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
|
@ -102,51 +103,8 @@ func GetLogin(c web.C, w http.ResponseWriter, r *http.Request) {
|
|||
redirect = "/sync"
|
||||
log.Println("sync user account.", u.Login)
|
||||
|
||||
// sync inside a goroutine. This should eventually be moved to
|
||||
// its own package / sync utility.
|
||||
go func() {
|
||||
repos, err := remote.GetRepos(u)
|
||||
if err != nil {
|
||||
log.Println("Error syncing user account, listing repositories", u.Login, err)
|
||||
return
|
||||
}
|
||||
|
||||
// insert all repositories
|
||||
for _, repo := range repos {
|
||||
var role = repo.Role
|
||||
if err := datastore.PostRepo(ctx, repo); err != nil {
|
||||
// typically we see a failure because the repository already exists
|
||||
// in which case, we can retrieve the existing record to get the ID.
|
||||
repo, err = datastore.GetRepoName(ctx, repo.Host, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
log.Println("Error adding repo.", u.Login, repo.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// add user permissions
|
||||
perm := model.Perm{
|
||||
UserID: u.ID,
|
||||
RepoID: repo.ID,
|
||||
Read: role.Read,
|
||||
Write: role.Write,
|
||||
Admin: role.Admin,
|
||||
}
|
||||
if err := datastore.PostPerm(ctx, &perm); err != nil {
|
||||
log.Println("Error adding permissions.", u.Login, repo.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("Successfully syced repo.", u.Login+"/"+repo.Name)
|
||||
}
|
||||
|
||||
u.Synced = time.Now().UTC().Unix()
|
||||
u.Syncing = false
|
||||
if err := datastore.PutUser(ctx, u); err != nil {
|
||||
log.Println("Error syncing user account, updating sync date", u.Login, err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
// sync inside a goroutine
|
||||
go sync.SyncUser(ctx, u, remote)
|
||||
}
|
||||
|
||||
token, err := session.GenerateToken(ctx, r, u)
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/plugin/remote"
|
||||
"github.com/drone/drone/server/datastore"
|
||||
"github.com/drone/drone/server/sync"
|
||||
"github.com/drone/drone/shared/model"
|
||||
"github.com/goji/context"
|
||||
"github.com/zenazn/goji/web"
|
||||
|
@ -111,3 +113,37 @@ func GetUserFeed(c web.C, w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
json.NewEncoder(w).Encode(&repos)
|
||||
}
|
||||
|
||||
// PostUserSync accepts a request to post user sync
|
||||
//
|
||||
// POST /api/user/sync
|
||||
//
|
||||
func PostUserSync(c web.C, w http.ResponseWriter, r *http.Request) {
|
||||
var ctx = context.FromC(c)
|
||||
var user = ToUser(c)
|
||||
if user == nil {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var remote = remote.Lookup(user.Remote)
|
||||
if remote == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
if user.Syncing {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
return
|
||||
}
|
||||
|
||||
user.Syncing = true
|
||||
if err := datastore.PutUser(ctx, user); err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
go sync.SyncUser(ctx, user, remote)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -58,6 +58,7 @@ func New() *web.Mux {
|
|||
user.Use(middleware.RequireUser)
|
||||
user.Get("/api/user/feed", handler.GetUserFeed)
|
||||
user.Get("/api/user/repos", handler.GetUserRepos)
|
||||
user.Post("/api/user/sync", handler.PostUserSync)
|
||||
user.Get("/api/user", handler.GetUserCurrent)
|
||||
user.Put("/api/user", handler.PutUser)
|
||||
mux.Handle("/api/user*", user)
|
||||
|
|
55
server/sync/sync.go
Normal file
55
server/sync/sync.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
package sync
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"code.google.com/p/go.net/context"
|
||||
"github.com/drone/drone/plugin/remote"
|
||||
"github.com/drone/drone/server/datastore"
|
||||
"github.com/drone/drone/shared/model"
|
||||
)
|
||||
|
||||
func SyncUser(ctx context.Context, user *model.User, remote remote.Remote) {
|
||||
repos, err := remote.GetRepos(user)
|
||||
if err != nil {
|
||||
log.Println("Error syncing user account, listing repositories", user.Login, err)
|
||||
return
|
||||
}
|
||||
|
||||
// insert all repositories
|
||||
for _, repo := range repos {
|
||||
var role = repo.Role
|
||||
if err := datastore.PostRepo(ctx, repo); err != nil {
|
||||
// typically we see a failure because the repository already exists
|
||||
// in which case, we can retrieve the existing record to get the ID.
|
||||
repo, err = datastore.GetRepoName(ctx, repo.Host, repo.Owner, repo.Name)
|
||||
if err != nil {
|
||||
log.Println("Error adding repo.", user.Login, repo.Name, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// add user permissions
|
||||
perm := model.Perm{
|
||||
UserID: user.ID,
|
||||
RepoID: repo.ID,
|
||||
Read: role.Read,
|
||||
Write: role.Write,
|
||||
Admin: role.Admin,
|
||||
}
|
||||
if err := datastore.PostPerm(ctx, &perm); err != nil {
|
||||
log.Println("Error adding permissions.", user.Login, repo.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("Successfully syced repo.", user.Login+"/"+repo.Name)
|
||||
}
|
||||
|
||||
user.Synced = time.Now().UTC().Unix()
|
||||
user.Syncing = false
|
||||
if err := datastore.PutUser(ctx, user); err != nil {
|
||||
log.Println("Error syncing user account, updating sync date", user.Login, err)
|
||||
return
|
||||
}
|
||||
}
|
|
@ -214,12 +214,8 @@ func (c *Client) do(method, path string, in, out interface{}) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Read the bytes from the body (make sure we defer close the body)
|
||||
// make sure we defer close the body
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check for an http error status (ie not 200 StatusOK)
|
||||
switch resp.StatusCode {
|
||||
|
@ -233,9 +229,9 @@ func (c *Client) do(method, path string, in, out interface{}) error {
|
|||
return ErrBadRequest
|
||||
}
|
||||
|
||||
// Unmarshall the JSON response
|
||||
// Decode the JSON response
|
||||
if out != nil {
|
||||
return json.Unmarshal(body, out)
|
||||
return json.NewDecoder(resp.Body).Decode(out)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
Loading…
Reference in a new issue