mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 19:31:05 +00:00
moved to single binary project structure
This commit is contained in:
parent
76ea19aca0
commit
155576fb03
139 changed files with 0 additions and 9815 deletions
|
@ -1 +0,0 @@
|
|||
This is where Drone binaries go after running "make" in the root directory.
|
|
@ -1,261 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/namsral/flag"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/elazarl/go-bindata-assetfs"
|
||||
"github.com/drone/drone/pkg/remote"
|
||||
"github.com/drone/drone/pkg/server"
|
||||
|
||||
log "github.com/drone/drone/Godeps/_workspace/src/github.com/Sirupsen/logrus"
|
||||
eventbus "github.com/drone/drone/pkg/bus/builtin"
|
||||
queue "github.com/drone/drone/pkg/queue/builtin"
|
||||
runner "github.com/drone/drone/pkg/runner/builtin"
|
||||
"github.com/drone/drone/pkg/store"
|
||||
|
||||
_ "github.com/drone/drone/pkg/remote/builtin/github"
|
||||
_ "github.com/drone/drone/pkg/remote/builtin/gitlab"
|
||||
_ "github.com/drone/drone/pkg/store/builtin"
|
||||
)
|
||||
|
||||
var (
|
||||
// commit sha for the current build, set by
|
||||
// the compile process.
|
||||
version string
|
||||
revision string
|
||||
)
|
||||
|
||||
var conf = struct {
|
||||
debug bool
|
||||
|
||||
server struct {
|
||||
addr string
|
||||
cert string
|
||||
key string
|
||||
}
|
||||
|
||||
docker struct {
|
||||
host string
|
||||
cert string
|
||||
key string
|
||||
ca string
|
||||
}
|
||||
|
||||
remote struct {
|
||||
driver string
|
||||
config string
|
||||
}
|
||||
|
||||
database struct {
|
||||
driver string
|
||||
config string
|
||||
}
|
||||
|
||||
plugin struct {
|
||||
filter string
|
||||
}
|
||||
}{}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.StringVar(&conf.docker.host, "docker-host", "unix:///var/run/docker.sock", "")
|
||||
flag.StringVar(&conf.docker.cert, "docker-cert", "", "")
|
||||
flag.StringVar(&conf.docker.key, "docker-key", "", "")
|
||||
flag.StringVar(&conf.docker.ca, "docker-ca", "", "")
|
||||
flag.StringVar(&conf.server.addr, "server-addr", ":8080", "")
|
||||
flag.StringVar(&conf.server.cert, "server-cert", "", "")
|
||||
flag.StringVar(&conf.server.key, "server-key", "", "")
|
||||
flag.StringVar(&conf.remote.driver, "remote-driver", "github", "")
|
||||
flag.StringVar(&conf.remote.config, "remote-config", "https://github.com", "")
|
||||
flag.StringVar(&conf.database.driver, "database-driver", "sqlite3", "")
|
||||
flag.StringVar(&conf.database.config, "database-config", "drone.sqlite", "")
|
||||
flag.StringVar(&conf.plugin.filter, "plugin-filter", "plugins/*", "")
|
||||
flag.BoolVar(&conf.debug, "debug", false, "")
|
||||
|
||||
flag.String("config", "", "")
|
||||
flag.Parse()
|
||||
|
||||
store, err := store.New(conf.database.driver, conf.database.config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
remote, err := remote.New(conf.remote.driver, conf.remote.config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
eventbus_ := eventbus.New()
|
||||
queue_ := queue.New()
|
||||
updater := runner.NewUpdater(eventbus_, store, remote)
|
||||
runner_ := runner.Runner{Updater: updater}
|
||||
|
||||
// launch the local queue runner if the system
|
||||
// is not conifugred to run in agent mode
|
||||
go run(&runner_, queue_)
|
||||
|
||||
r := gin.Default()
|
||||
|
||||
api := r.Group("/api")
|
||||
api.Use(server.SetHeaders())
|
||||
api.Use(server.SetBus(eventbus_))
|
||||
api.Use(server.SetDatastore(store))
|
||||
api.Use(server.SetRemote(remote))
|
||||
api.Use(server.SetQueue(queue_))
|
||||
api.Use(server.SetUser())
|
||||
api.Use(server.SetRunner(&runner_))
|
||||
api.OPTIONS("/*path", func(c *gin.Context) {})
|
||||
|
||||
user := api.Group("/user")
|
||||
{
|
||||
user.Use(server.MustUser())
|
||||
|
||||
user.GET("", server.GetUserCurr)
|
||||
user.PATCH("", server.PutUserCurr)
|
||||
user.GET("/feed", server.GetUserFeed)
|
||||
user.GET("/repos", server.GetUserRepos)
|
||||
user.POST("/token", server.PostUserToken)
|
||||
}
|
||||
|
||||
users := api.Group("/users")
|
||||
{
|
||||
users.Use(server.MustAdmin())
|
||||
|
||||
users.GET("", server.GetUsers)
|
||||
users.GET("/:name", server.GetUser)
|
||||
users.POST("/:name", server.PostUser)
|
||||
users.PATCH("/:name", server.PutUser)
|
||||
users.DELETE("/:name", server.DeleteUser)
|
||||
}
|
||||
|
||||
repos := api.Group("/repos/:owner/:name")
|
||||
{
|
||||
repos.POST("", server.PostRepo)
|
||||
|
||||
repo := repos.Group("")
|
||||
{
|
||||
repo.Use(server.SetRepo())
|
||||
repo.Use(server.SetPerm())
|
||||
repo.Use(server.CheckPull())
|
||||
repo.Use(server.CheckPush())
|
||||
|
||||
repo.GET("", server.GetRepo)
|
||||
repo.PATCH("", server.PutRepo)
|
||||
repo.DELETE("", server.DeleteRepo)
|
||||
repo.POST("/encrypt", server.Encrypt)
|
||||
repo.POST("/watch", server.Subscribe)
|
||||
repo.DELETE("/unwatch", server.Unsubscribe)
|
||||
|
||||
repo.GET("/builds", server.GetBuilds)
|
||||
repo.GET("/builds/:number", server.GetBuild)
|
||||
repo.POST("/builds/:number", server.RunBuild)
|
||||
repo.DELETE("/builds/:number", server.KillBuild)
|
||||
repo.GET("/logs/:number/:task", server.GetLogs)
|
||||
// repo.POST("/status/:number", server.PostBuildStatus)
|
||||
}
|
||||
}
|
||||
|
||||
badges := api.Group("/badges/:owner/:name")
|
||||
{
|
||||
badges.Use(server.SetRepo())
|
||||
|
||||
badges.GET("/status.svg", server.GetBadge)
|
||||
badges.GET("/cc.xml", server.GetCC)
|
||||
}
|
||||
|
||||
hooks := api.Group("/hook")
|
||||
{
|
||||
hooks.POST("", server.PostHook)
|
||||
}
|
||||
|
||||
stream := api.Group("/stream")
|
||||
{
|
||||
stream.Use(server.SetRepo())
|
||||
stream.Use(server.SetPerm())
|
||||
stream.GET("/:owner/:name", server.GetRepoEvents)
|
||||
stream.GET("/:owner/:name/:build/:number", server.GetStream)
|
||||
|
||||
}
|
||||
|
||||
auth := r.Group("/authorize")
|
||||
{
|
||||
auth.Use(server.SetHeaders())
|
||||
auth.Use(server.SetDatastore(store))
|
||||
auth.Use(server.SetRemote(remote))
|
||||
auth.GET("", server.GetLogin)
|
||||
auth.POST("", server.GetLogin)
|
||||
}
|
||||
|
||||
gitlab := r.Group("/gitlab/:owner/:name")
|
||||
{
|
||||
gitlab.Use(server.SetDatastore(store))
|
||||
gitlab.Use(server.SetRepo())
|
||||
|
||||
gitlab.GET("/commits/:sha", server.GetCommit)
|
||||
gitlab.GET("/pulls/:number", server.GetPullRequest)
|
||||
|
||||
redirects := gitlab.Group("/redirect")
|
||||
{
|
||||
redirects.GET("/commits/:sha", server.RedirectSha)
|
||||
redirects.GET("/pulls/:number", server.RedirectPullRequest)
|
||||
}
|
||||
}
|
||||
|
||||
r.SetHTMLTemplate(index())
|
||||
r.NoRoute(func(c *gin.Context) {
|
||||
c.HTML(200, "index.html", nil)
|
||||
})
|
||||
|
||||
http.Handle("/static/", static())
|
||||
http.Handle("/", r)
|
||||
|
||||
if len(conf.server.cert) == 0 {
|
||||
err = http.ListenAndServe(conf.server.addr, nil)
|
||||
} else {
|
||||
err = http.ListenAndServeTLS(conf.server.addr, conf.server.cert, conf.server.key, nil)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Cannot start server: ", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// static is a helper function that will setup handlers
|
||||
// for serving static files.
|
||||
func static() http.Handler {
|
||||
// default file server is embedded
|
||||
var handler = http.FileServer(&assetfs.AssetFS{
|
||||
Asset: Asset,
|
||||
AssetDir: AssetDir,
|
||||
Prefix: "cmd/drone-server/static",
|
||||
})
|
||||
if conf.debug {
|
||||
handler = http.FileServer(
|
||||
http.Dir("cmd/drone-server/static"),
|
||||
)
|
||||
}
|
||||
return http.StripPrefix("/static/", handler)
|
||||
}
|
||||
|
||||
// index is a helper function that will setup a template
|
||||
// for rendering the main angular index.html file.
|
||||
func index() *template.Template {
|
||||
file := MustAsset("cmd/drone-server/static/index.html")
|
||||
filestr := string(file)
|
||||
return template.Must(template.New("index.html").Parse(filestr))
|
||||
}
|
||||
|
||||
// run is a helper function for initializing the
|
||||
// built-in build runner, if not running in remote
|
||||
// mode.
|
||||
func run(r *runner.Runner, q *queue.Queue) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
r.Poll(q)
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 557 B |
|
@ -1,93 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="43"
|
||||
height="36.350704"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="drone-logo-no-circle.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#424242"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8"
|
||||
inkscape:cx="26.576205"
|
||||
inkscape:cy="-72.54425"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:snap-global="false"
|
||||
inkscape:window-width="1295"
|
||||
inkscape:window-height="744"
|
||||
inkscape:window-x="65"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-maximized="1"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid2996"
|
||||
empspacing="5"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true"
|
||||
originx="-21.720779px"
|
||||
originy="-990.37188px" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-21.720779,-25.639593)">
|
||||
<path
|
||||
sodipodi:type="arc"
|
||||
style="fill:#424242;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
id="path2998"
|
||||
sodipodi:cx="172.10474"
|
||||
sodipodi:cy="458.39249"
|
||||
sodipodi:rx="5.4295697"
|
||||
sodipodi:ry="5.0507627"
|
||||
d="m 177.53431,458.39249 c 0,2.78946 -2.43091,5.05076 -5.42957,5.05076 -2.99867,0 -5.42957,-2.2613 -5.42957,-5.05076 0,-2.78946 2.4309,-5.05077 5.42957,-5.05077 2.99866,0 5.42957,2.26131 5.42957,5.05077 z"
|
||||
transform="matrix(1.0129716,0,0,1.0889445,-131.11643,-452.42373)" />
|
||||
<path
|
||||
style="fill:#424242;fill-opacity:1;stroke-width:0;stroke-miterlimit:4"
|
||||
d="m 43.220779,25.640247 c 9.60163,0.0752 20.51786,6.8438 21.5,19.6 l -13,0 c 0,0 -1.67472,-7.04733 -8.5,-7 -6.82528,0.0473 -8.5,7 -8.5,7 l -13,0 c 0.63161,-12.53073 11.36576,-19.67935 21.5,-19.6 z"
|
||||
id="rect3810"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="scczccs" />
|
||||
<path
|
||||
style="fill:#424242;fill-opacity:1;stroke-width:0;stroke-miterlimit:4"
|
||||
d="m 43.310069,61.990247 c -7.159395,0.01905 -13.847588,-5.383347 -16.58929,-13.75 l 8,0 c 0,0 1.72575,6.96782 8.55103,6.92049 6.82528,-0.0473 8.44897,-6.92049 8.44897,-6.92049 l 8,0 c -1.783351,8.850973 -9.251314,13.730946 -16.41071,13.75 z"
|
||||
id="rect3810-1"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="scczccs" />
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 3.4 KiB |
|
@ -1,46 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html ng-app="drone" lang="en">
|
||||
<head>
|
||||
|
||||
<base href="/"/>
|
||||
<link rel="stylesheet" href="/static/styles/reset.css" />
|
||||
<link rel="stylesheet" href="/static/styles/fonts.css" />
|
||||
<link rel="stylesheet" href="/static/styles/alert.css" />
|
||||
<link rel="stylesheet" href="/static/styles/blankslate.css" />
|
||||
<link rel="stylesheet" href="/static/styles/list.css" />
|
||||
<link rel="stylesheet" href="/static/styles/label.css" />
|
||||
<link rel="stylesheet" href="/static/styles/range.css" />
|
||||
<link rel="stylesheet" href="/static/styles/switch.css" />
|
||||
<link rel="stylesheet" href="/static/styles/main.css" />
|
||||
<link rel="icon" href="/static/favicon.png">
|
||||
</head>
|
||||
<body>
|
||||
<div ui-view="layout"></div>
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular-route.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.3.15/angular-resource.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui/0.4.0/angular-ui.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.15/angular-ui-router.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.6.0/moment.min.js"></script>
|
||||
|
||||
<!-- main javascript application -->
|
||||
<script src="/static/scripts/term.js"></script>
|
||||
<script src="/static/scripts/drone.js"></script>
|
||||
<script src="/static/scripts/controllers/repos.js"></script>
|
||||
<script src="/static/scripts/controllers/builds.js"></script>
|
||||
<script src="/static/scripts/controllers/users.js"></script>
|
||||
|
||||
<script src="/static/scripts/services/repos.js"></script>
|
||||
<script src="/static/scripts/services/builds.js"></script>
|
||||
<script src="/static/scripts/services/users.js"></script>
|
||||
<script src="/static/scripts/services/logs.js"></script>
|
||||
<script src="/static/scripts/services/tokens.js"></script>
|
||||
<script src="/static/scripts/services/feed.js"></script>
|
||||
|
||||
<script src="/static/scripts/filters/filter.js"></script>
|
||||
<script src="/static/scripts/filters/gravatar.js"></script>
|
||||
<script src="/static/scripts/filters/time.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,245 +0,0 @@
|
|||
(function () {
|
||||
|
||||
/**
|
||||
* BuildsCtrl responsible for rendering the repo's
|
||||
* recent build history.
|
||||
*/
|
||||
function BuildsCtrl($scope, $stateParams, builds, repos, users, logs) {
|
||||
var owner = $stateParams.owner;
|
||||
var name = $stateParams.name;
|
||||
var fullName = owner + '/' + name;
|
||||
|
||||
$scope.loading=true;
|
||||
|
||||
// Gets the currently authenticated user
|
||||
// users.getCached().then(function (payload) {
|
||||
// $scope.user = payload.data;
|
||||
// });
|
||||
|
||||
// Gets a repository
|
||||
repos.get(fullName).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
$scope.loading=false;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
// Gets a list of builds
|
||||
builds.list(fullName).then(function (payload) {
|
||||
$scope.builds = angular.isArray(payload.data) ? payload.data : [];
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
$scope.watch = function (repo) {
|
||||
repos.watch(repo.full_name).then(function (payload) {
|
||||
$scope.repo.starred = true;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.unwatch = function (repo) {
|
||||
repos.unwatch(repo.full_name).then(function () {
|
||||
$scope.repo.starred = false;
|
||||
});
|
||||
};
|
||||
|
||||
repos.subscribe(fullName, function (event) {
|
||||
var added = false;
|
||||
for (var i = 0; i < $scope.builds.length; i++) {
|
||||
var build = $scope.builds[i];
|
||||
if (event.number !== build.number) {
|
||||
continue; // ignore
|
||||
}
|
||||
// update the build status
|
||||
$scope.builds[i] = event;
|
||||
$scope.$apply();
|
||||
added = true;
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
$scope.builds.push(event);
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* BuildCtrl responsible for rendering a build.
|
||||
*/
|
||||
function BuildCtrl($scope, $stateParams, $window, logs, builds, repos, users) {
|
||||
var number = $stateParams.number;
|
||||
var owner = $stateParams.owner;
|
||||
var name = $stateParams.name;
|
||||
var fullName = owner + '/' + name;
|
||||
var step = parseInt($stateParams.step) || 1;
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCached().then(function (payload) {
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
// Gets a repository
|
||||
repos.get(fullName).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
// Gets the build
|
||||
builds.get(fullName, number).then(function (payload) {
|
||||
$scope.step = step;
|
||||
$scope.build = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
repos.subscribe(fullName, function (event) {
|
||||
if (event.number !== parseInt(number)) {
|
||||
return; // ignore
|
||||
}
|
||||
// update the build
|
||||
$scope.build = event;
|
||||
$scope.$apply();
|
||||
});
|
||||
|
||||
$scope.restart = function () {
|
||||
builds.restart(fullName, number).then(function (payload) {
|
||||
$scope.build = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
builds.cancel(fullName, number).then(function (payload) {
|
||||
$scope.build = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.tail = function () {
|
||||
tail = !tail;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* BuildOutCtrl responsible for rendering a build output.
|
||||
*/
|
||||
function BuildOutCtrl($scope, $stateParams, $window, logs, builds, repos, users) {
|
||||
|
||||
var step = parseInt($stateParams.step) || 1;
|
||||
var number = $stateParams.number;
|
||||
var owner = $stateParams.owner;
|
||||
var name = $stateParams.name;
|
||||
var fullName = owner + '/' + name;
|
||||
var streaming = false;
|
||||
var tail = false;
|
||||
|
||||
// Initiates streaming a build.
|
||||
var stream = function () {
|
||||
if (streaming) {
|
||||
return;
|
||||
}
|
||||
streaming = true;
|
||||
|
||||
var convert = new Filter({stream: true, newline: false});
|
||||
var term = document.getElementById("term");
|
||||
term.innerHTML = "";
|
||||
|
||||
// subscribes to the build otuput.
|
||||
logs.subscribe(fullName, number, step, function (data) {
|
||||
term.innerHTML += convert.toHtml(data.replace("\\n", "\n"));
|
||||
if (tail) {
|
||||
// scrolls to the bottom of the page if enabled
|
||||
$window.scrollTo(0, $window.document.body.scrollHeight);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Gets the currently authenticated user
|
||||
// users.getCached().then(function (payload) {
|
||||
// $scope.user = payload.data;
|
||||
// });
|
||||
|
||||
// Gets a repository
|
||||
repos.get(fullName).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
// Gets the build
|
||||
builds.get(fullName, number).then(function (payload) {
|
||||
$scope.build = payload.data;
|
||||
$scope.task = payload.data.jobs[step - 1];
|
||||
$scope.step = step;
|
||||
|
||||
if (['pending', 'killed'].indexOf($scope.task.status) !== -1) {
|
||||
// do nothing
|
||||
} else if ($scope.task.status === 'running') {
|
||||
// stream the build
|
||||
stream();
|
||||
} else {
|
||||
|
||||
// fetch the logs for the finished build.
|
||||
logs.get(fullName, number, step).then(function (payload) {
|
||||
var convert = new Filter({stream: false, newline: false});
|
||||
var term = document.getElementById("term")
|
||||
term.innerHTML = convert.toHtml(payload.data);
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
}
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
repos.subscribe(fullName, function (event) {
|
||||
if (event.number !== parseInt(number)) {
|
||||
return; // ignore
|
||||
}
|
||||
// update the build
|
||||
$scope.build = event;
|
||||
$scope.task = event.jobs[step - 1];
|
||||
$scope.$apply();
|
||||
|
||||
// start streaming the current build
|
||||
if ($scope.task.status === 'running') {
|
||||
stream();
|
||||
} else {
|
||||
// resets our streaming state
|
||||
streaming = false;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.restart = function () {
|
||||
builds.restart(fullName, number).then(function (payload) {
|
||||
$scope.build = payload.data;
|
||||
$scope.task = payload.data.jobs[step - 1];
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
builds.cancel(fullName, number).then(function (payload) {
|
||||
$scope.build = payload.data;
|
||||
$scope.task = payload.data.builds[step - 1];
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.tail = function () {
|
||||
tail = !tail;
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.controller('BuildOutCtrl', BuildOutCtrl)
|
||||
.controller('BuildCtrl', BuildCtrl)
|
||||
.controller('BuildsCtrl', BuildsCtrl);
|
||||
})();
|
|
@ -1,67 +0,0 @@
|
|||
(function () {
|
||||
|
||||
/**
|
||||
* CommitsCtrl responsible for rendering the repo's
|
||||
* recent commit history.
|
||||
*/
|
||||
function CommitsCtrl($scope, $stateParams, builds, repos, users, logs) {
|
||||
|
||||
var owner = $stateParams.owner;
|
||||
var name = $stateParams.name;
|
||||
var fullName = owner+'/'+name;
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCached().then(function(payload){
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
// Gets a repository
|
||||
repos.get(fullName).then(function(payload){
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function(err){
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
// Gets a list of commits
|
||||
builds.list(fullName).then(function(payload){
|
||||
$scope.builds = angular.isArray(payload.data) ? payload.data : [];
|
||||
}).catch(function(err){
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
$scope.watch = function(repo) {
|
||||
repos.watch(repo.full_name).then(function(payload) {
|
||||
$scope.repo.starred = true;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.unwatch = function(repo) {
|
||||
repos.unwatch(repo.full_name).then(function() {
|
||||
$scope.repo.starred = false;
|
||||
});
|
||||
}
|
||||
|
||||
repos.subscribe(fullName, function(event) {
|
||||
var added = false;
|
||||
for (var i=0;i<$scope.builds.length;i++) {
|
||||
var build = $scope.builds[i];
|
||||
if (event.number !== build.number) {
|
||||
continue; // ignore
|
||||
}
|
||||
// update the build status
|
||||
$scope.builds[i] = event;
|
||||
$scope.$apply();
|
||||
added = true;
|
||||
}
|
||||
|
||||
if (!added) {
|
||||
$scope.builds.push(event);
|
||||
$scope.$apply();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.controller('CommitsCtrl', CommitsCtrl)
|
||||
})();
|
|
@ -1,150 +0,0 @@
|
|||
(function () {
|
||||
|
||||
/**
|
||||
* ReposCtrl responsible for rendering the user's
|
||||
* repository home screen.
|
||||
*/
|
||||
function ReposCtrl($scope, $location, $stateParams, repos, users) {
|
||||
$scope.loading = true;
|
||||
$scope.waiting = false;
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCached().then(function (payload) {
|
||||
$scope.user = payload.data;
|
||||
$scope.loading = false;
|
||||
});
|
||||
|
||||
// Gets a list of repos to display in the
|
||||
// dropdown.
|
||||
repos.list().then(function (payload) {
|
||||
$scope.repos = angular.isArray(payload.data) ? payload.data : [];
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
// Adds a repository
|
||||
$scope.add = function (event, fullName) {
|
||||
$scope.error = undefined;
|
||||
if (event.which && event.which !== 13) {
|
||||
return;
|
||||
}
|
||||
$scope.waiting = true;
|
||||
|
||||
repos.post(fullName).then(function (payload) {
|
||||
$location.path('/' + fullName);
|
||||
$scope.waiting = false;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
$scope.waiting = false;
|
||||
$scope.search_text = undefined;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RepoAddCtrl responsible for activaing a new
|
||||
* repository.
|
||||
*/
|
||||
function RepoAddCtrl($scope, $location, repos, users) {
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCached().then(function (payload) {
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
$scope.add = function (slug) {
|
||||
repos.post(slug).then(function (payload) {
|
||||
$location.path('/' + slug);
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RepoEditCtrl responsible for editing a repository.
|
||||
*/
|
||||
function RepoEditCtrl($scope, $window, $location, $stateParams, repos, users) {
|
||||
var owner = $stateParams.owner;
|
||||
var name = $stateParams.name;
|
||||
var fullName = owner + '/' + name;
|
||||
|
||||
// Inject window for composing url
|
||||
$scope.window = $window;
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCached().then(function (payload) {
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
// Gets a repository
|
||||
repos.get(fullName).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
|
||||
$scope.save = function (repo) {
|
||||
repo.timeout = parseInt(repo.timeout);
|
||||
repos.update(repo).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.delete = function (repo) {
|
||||
repos.delete(repo).then(function (payload) {
|
||||
$location.path('/');
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.param = {};
|
||||
$scope.addParam = function (param) {
|
||||
if (!$scope.repo.params) {
|
||||
$scope.repo.params = {}
|
||||
}
|
||||
$scope.repo.params[param.key] = param.value;
|
||||
$scope.param = {};
|
||||
|
||||
// auto-update
|
||||
repos.update($scope.repo).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
$scope.encrypt = function (plaintext) {
|
||||
repos.encrypt(fullName, plaintext).then(function (payload) {
|
||||
$scope.secure = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteParam = function (key) {
|
||||
delete $scope.repo.params[key];
|
||||
|
||||
// auto-update
|
||||
repos.update($scope.repo).then(function (payload) {
|
||||
$scope.repo = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toSnakeCase(str) {
|
||||
return str.replace(/ /g, '_').replace(/([a-z0-9])([A-Z0-9])/g, '$1_$2').toLowerCase();
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.controller('ReposCtrl', ReposCtrl)
|
||||
.controller('RepoAddCtrl', RepoAddCtrl)
|
||||
.controller('RepoEditCtrl', RepoEditCtrl);
|
||||
})();
|
|
@ -1,119 +0,0 @@
|
|||
(function () {
|
||||
|
||||
function UserHeaderCtrl($scope, $stateParams, users) {
|
||||
// Gets the currently authenticated user
|
||||
users.getCurrent().then(function(payload){
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
$scope.number = $stateParams.number || undefined;
|
||||
$scope.owner = $stateParams.owner || undefined;
|
||||
$scope.name = $stateParams.name || undefined;
|
||||
$scope.full_name = $scope.owner + '/' + $scope.name;
|
||||
}
|
||||
|
||||
function UserLoginCtrl($scope, $window) {
|
||||
// attempts to extract an error message from
|
||||
// the URL hash in format #error=?
|
||||
$scope.error = $window.location.hash.substr(7);
|
||||
}
|
||||
|
||||
function UserLogoutCtrl($scope, $window, $state) {
|
||||
// Remove login information from the local
|
||||
// storage and redirect to login page
|
||||
if (localStorage.hasOwnProperty("access_token")) {
|
||||
localStorage.removeItem("access_token");
|
||||
}
|
||||
|
||||
$state.go("login", {}, {
|
||||
location: "replace"
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* UserCtrl is responsible for managing user settings.
|
||||
*/
|
||||
function UserCtrl($scope, users, tokens) {
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCurrent().then(function(payload){
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
$scope.showToken = function() {
|
||||
tokens.post().then(function(payload) {
|
||||
$scope.token = payload.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UsersCtrl is responsible for managing user accounts.
|
||||
* This part of the site is for administrators only.
|
||||
*/
|
||||
function UsersCtrl($scope, users) {
|
||||
$scope.loading = true;
|
||||
$scope.waiting = false;
|
||||
|
||||
// Gets the currently authenticated user
|
||||
users.getCached().then(function(payload){
|
||||
$scope.user = payload.data;
|
||||
});
|
||||
|
||||
// Gets the list of all system users
|
||||
users.list().then(function(payload){
|
||||
$scope.loading = true;
|
||||
$scope.users = payload.data;
|
||||
});
|
||||
|
||||
$scope.add = function(event, login) {
|
||||
$scope.error = undefined;
|
||||
$scope.new_user = undefined;
|
||||
if (event.which && event.which !== 13) {
|
||||
return;
|
||||
}
|
||||
$scope.waiting = true;
|
||||
|
||||
users.post(login).then(function(payload){
|
||||
$scope.users.push(payload.data);
|
||||
$scope.search_text=undefined;
|
||||
$scope.waiting = false;
|
||||
$scope.new_user = payload.data;
|
||||
}).catch(function (err) {
|
||||
$scope.error = err;
|
||||
$scope.waiting = false;
|
||||
$scope.search_text = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
$scope.toggle = function(user) {
|
||||
if (user.login === $scope.user.login) {
|
||||
// cannot revoke admin privilege for self
|
||||
$scope.error = {}; // todo display an actual error here
|
||||
return;
|
||||
}
|
||||
user.admin = !user.admin;
|
||||
users.put(user);
|
||||
}
|
||||
|
||||
$scope.remove = function(user) {
|
||||
if (user.login === $scope.user.login) {
|
||||
// cannot delete self
|
||||
$scope.error = {}; // todo display an actual error here
|
||||
return;
|
||||
}
|
||||
users.delete(user).then(function(){
|
||||
var index = $scope.users.indexOf(user);
|
||||
$scope.users.splice(index, 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.controller('UserHeaderCtrl', UserHeaderCtrl)
|
||||
.controller('UserLoginCtrl', UserLoginCtrl)
|
||||
.controller('UserLogoutCtrl', UserLogoutCtrl)
|
||||
.controller('UserCtrl', UserCtrl)
|
||||
.controller('UsersCtrl', UsersCtrl);
|
||||
})();
|
|
@ -1,283 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* Creates the angular application.
|
||||
*/
|
||||
angular.module('drone', [
|
||||
'ngRoute',
|
||||
'ui.filters',
|
||||
'ui.router'
|
||||
]);
|
||||
|
||||
/**
|
||||
* Bootstraps the application and retrieves the
|
||||
* token from the
|
||||
*/
|
||||
function Authorize() {
|
||||
// First, parse the query string
|
||||
var params = {}, queryString = location.hash.substring(1),
|
||||
regex = /([^&=]+)=([^&]*)/g, m;
|
||||
|
||||
// Loop through and retrieve the token
|
||||
while (m = regex.exec(queryString)) {
|
||||
params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
|
||||
}
|
||||
|
||||
// if the user has just received an auth token we
|
||||
// should extract from the URL, save to local storage
|
||||
// and then remove from the URL for good measure.
|
||||
if (params.access_token) {
|
||||
localStorage.setItem("access_token", params.access_token);
|
||||
history.replaceState({}, document.title, location.pathname);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the route configuration for the
|
||||
* main application.
|
||||
*/
|
||||
function Config($stateProvider, $httpProvider, $locationProvider) {
|
||||
|
||||
// Resolver that will attempt to load the currently
|
||||
// authenticated user prior to loading the page.
|
||||
var resolveUser = {
|
||||
user: function (users) {
|
||||
return users.getCached();
|
||||
}
|
||||
};
|
||||
|
||||
$stateProvider
|
||||
.state('app', {
|
||||
abstract: true,
|
||||
views: {
|
||||
'layout': {
|
||||
templateUrl: '/static/scripts/views/layout.html',
|
||||
controller: function ($scope, $routeParams, repos, users) {
|
||||
users.getCached().then(function (payload) {
|
||||
if (payload && payload.data) {
|
||||
$scope.user = payload.data;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.state('app.index', {
|
||||
url: '/',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/repos/index/toolbar.html'
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/repos/index/content.html',
|
||||
controller: 'ReposCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Dashboard'
|
||||
})
|
||||
.state('login', {
|
||||
url: '/login',
|
||||
views: {
|
||||
'layout': {
|
||||
templateUrl: '/static/scripts/views/login.html',
|
||||
controller: 'UserLoginCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Login'
|
||||
})
|
||||
.state('logout', {
|
||||
url: '/logout',
|
||||
views: {
|
||||
'layout': {
|
||||
templateUrl: '/static/scripts/views/login.html',
|
||||
controller: 'UserLogoutCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Logout'
|
||||
})
|
||||
.state('app.profile', {
|
||||
url: '/profile',
|
||||
views: {
|
||||
'toolbar': {templateUrl: '/static/scripts/views/profile/toolbar.html'},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/profile/content.html',
|
||||
controller: 'UserCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Profile'
|
||||
})
|
||||
.state('app.users', {
|
||||
url: '/users',
|
||||
views: {
|
||||
'toolbar': {templateUrl: '/static/scripts/views/users/toolbar.html'},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/users/content.html',
|
||||
controller: 'UsersCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Users'
|
||||
})
|
||||
.state('app.builds', {
|
||||
url: '/:owner/:name',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/builds/index/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/builds/index/content.html',
|
||||
controller: 'BuildsCtrl'
|
||||
}
|
||||
},
|
||||
title: 'Builds'
|
||||
})
|
||||
.state('app.repo_edit', {
|
||||
url: '/:owner/:name/edit',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/repos/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/repos/edit.html',
|
||||
controller: 'RepoEditCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Edit Repository'
|
||||
})
|
||||
.state('app.repo_del', {
|
||||
url: '/:owner/:name/delete',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/repos/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/repos/del.html',
|
||||
controller: 'RepoEditCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Delete Repository'
|
||||
})
|
||||
.state('app.repo_env', {
|
||||
url: '/:owner/:name/edit/env',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/repos/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/repos/env.html',
|
||||
controller: 'RepoEditCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Private Vars'
|
||||
})
|
||||
.state('app.repo_secure', {
|
||||
url: '/:owner/:name/secure',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/repos/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/repos/secure.html',
|
||||
controller: 'RepoEditCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Secure Variables'
|
||||
})
|
||||
.state('app.build', {
|
||||
url: '/:owner/:name/:number',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/builds/show/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/builds/show/content.html',
|
||||
controller: 'BuildOutCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Build'
|
||||
})
|
||||
.state('app.job', {
|
||||
url: '/:owner/:name/:number/:step',
|
||||
views: {
|
||||
'toolbar': {
|
||||
templateUrl: '/static/scripts/views/builds/show/toolbar.html',
|
||||
controller: 'UserHeaderCtrl',
|
||||
resolve: resolveUser
|
||||
},
|
||||
'content': {
|
||||
templateUrl: '/static/scripts/views/builds/show/content.html',
|
||||
controller: 'BuildOutCtrl',
|
||||
resolve: resolveUser
|
||||
}
|
||||
},
|
||||
title: 'Build'
|
||||
});
|
||||
|
||||
// Enables html5 mode
|
||||
$locationProvider.html5Mode(true);
|
||||
|
||||
// Appends the Bearer token to authorize every
|
||||
// outbound http request.
|
||||
$httpProvider.defaults.headers.common.Authorization = 'Bearer ' + localStorage.getItem('access_token');
|
||||
|
||||
// Intercepts every oubput http response and redirects
|
||||
// the user to the logic screen if the request was rejected.
|
||||
$httpProvider.interceptors.push(function ($q, $location) {
|
||||
return {
|
||||
'responseError': function (rejection) {
|
||||
if (rejection.status === 401 && rejection.config.url !== "/api/user") {
|
||||
$location.path('/login');
|
||||
}
|
||||
if (rejection.status === 0) {
|
||||
// this happens when the app is down or
|
||||
// the browser loses internet connectivity.
|
||||
}
|
||||
return $q.reject(rejection);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function RouteChange($rootScope, repos, logs) {
|
||||
$rootScope.$on('$stateChangeStart', function () {
|
||||
repos.unsubscribe();
|
||||
logs.unsubscribe();
|
||||
});
|
||||
|
||||
$rootScope.$on('$stateChangeSuccess', function (event, current) {
|
||||
if (current.title) {
|
||||
document.title = current.title + ' · drone';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.config(Authorize)
|
||||
.config(Config)
|
||||
.run(RouteChange);
|
||||
|
||||
})();
|
|
@ -1,93 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
function trunc() {
|
||||
return function(str) {
|
||||
if (str && str.length > 10) {
|
||||
return str.substr(0, 10);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* author is a helper function that return the builds
|
||||
* commit or pull request author.
|
||||
*/
|
||||
function author() {
|
||||
return function(build) {
|
||||
if (!build) { return ""; }
|
||||
if (!build.head_commit && !build.pull_request) { return ""; }
|
||||
if (build.head_commit) { return build.head_commit.author.login || ""; }
|
||||
return build.pull_request.source.author.login;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sha is a helper function that return the builds sha.
|
||||
*/
|
||||
function sha() {
|
||||
return function(build) {
|
||||
if (!build) { return ""; }
|
||||
if (!build.head_commit && !build.pull_request) { return ""; }
|
||||
if (build.head_commit) { return build.head_commit.sha || ""; }
|
||||
return build.pull_request.source.sha;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ref is a helper function that return the builds sha.
|
||||
*/
|
||||
function ref() {
|
||||
return function(build) {
|
||||
if (!build) { return ""; }
|
||||
if (!build.head_commit && !build.pull_request) { return ""; }
|
||||
if (build.head_commit) { return build.head_commit.ref || ""; }
|
||||
return build.pull_request.source.ref;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* message is a helper function that return the builds message.
|
||||
*/
|
||||
function message() {
|
||||
return function(build) {
|
||||
if (!build) { return ""; }
|
||||
if (!build.head_commit && !build.pull_request) { return ""; }
|
||||
if (build.head_commit) { return build.head_commit.message || ""; }
|
||||
return build.pull_request.title || "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* message is a helper function that return the build icon.
|
||||
*/
|
||||
function icon() {
|
||||
return function(status) {
|
||||
switch(status) {
|
||||
case "pending":
|
||||
case "running":
|
||||
return "refresh";
|
||||
case "failure":
|
||||
return "clear";
|
||||
case "success":
|
||||
return "check";
|
||||
case "killed":
|
||||
case "error":
|
||||
return "remove";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.filter('trunc', trunc)
|
||||
.filter('author', author)
|
||||
.filter('message', message)
|
||||
.filter('sha', sha)
|
||||
.filter('icon', icon)
|
||||
.filter('ref', ref);
|
||||
|
||||
})();
|
|
@ -1,32 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* gravatar is a helper function that return the user's gravatar
|
||||
* image URL given an email hash.
|
||||
*/
|
||||
function gravatar() {
|
||||
return function(hash) {
|
||||
if (!hash) { return "http://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&f=y"; }
|
||||
return "https://secure.gravatar.com/avatar/"+hash+"?s=48&d=mm";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gravatarLarge is a helper function that return the user's gravatar
|
||||
* image URL given an email hash.
|
||||
*/
|
||||
function gravatarLarge() {
|
||||
return function(hash) {
|
||||
if (!hash) { return "http://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&f=y"; }
|
||||
return "https://secure.gravatar.com/avatar/"+hash+"?s=128&d=mm";
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.filter('gravatar', gravatar)
|
||||
.filter('gravatarLarge', gravatarLarge)
|
||||
|
||||
})();
|
|
@ -1,45 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* fromNow is a helper function that returns a human readable
|
||||
* string for the elapsed time between the given unix date and the
|
||||
* current time (ex. 10 minutes ago).
|
||||
*/
|
||||
function fromNow() {
|
||||
return function(date) {
|
||||
if (!date) {
|
||||
return;
|
||||
}
|
||||
return moment(new Date(date*1000)).fromNow();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* toDuration is a helper function that returns a human readable
|
||||
* string for the given duration in seconds (ex. 1 hour and 20 minutes).
|
||||
*/
|
||||
function toDuration() {
|
||||
return function(seconds) {
|
||||
return moment.duration(seconds, "seconds").humanize();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* toDate is a helper function that returns a human readable
|
||||
* string gor the given unix date.
|
||||
*/
|
||||
function toDate() {
|
||||
return function(date) {
|
||||
return moment(new Date(date*1000)).format('ll');
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.filter('fromNow', fromNow)
|
||||
.filter('toDate', toDate)
|
||||
.filter('toDuration', toDuration)
|
||||
|
||||
})();
|
|
@ -1,54 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* The BuildsService provides access to build
|
||||
* data using REST API calls.
|
||||
*/
|
||||
function BuildService($http, $window) {
|
||||
|
||||
/**
|
||||
* Gets a list of builds.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
*/
|
||||
this.list = function(repoName) {
|
||||
return $http.get('/api/repos/'+repoName+'/builds');
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a build.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
* @param {number} Number of the build.
|
||||
*/
|
||||
this.get = function(repoName, buildNumber) {
|
||||
return $http.get('/api/repos/'+repoName+'/builds/'+buildNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Restarts a build.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
* @param {number} Number of the build.
|
||||
*/
|
||||
this.restart = function(repoName, buildNumber) {
|
||||
return $http.post('/api/repos/' + repoName+'/builds/'+buildNumber);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancels a running build.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
* @param {number} Number of the build.
|
||||
*/
|
||||
this.cancel = function(repoName, buildNumber) {
|
||||
return $http.delete('/api/repos/'+repoName+'/builds/'+buildNumber);
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.service('builds', BuildService);
|
||||
})();
|
|
@ -1,40 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
function FeedService($http, $window) {
|
||||
|
||||
var callback,
|
||||
websocket,
|
||||
token = localStorage.getItem('access_token');
|
||||
|
||||
this.subscribe = function(_callback) {
|
||||
callback = _callback;
|
||||
|
||||
var proto = ($window.location.protocol === 'https:' ? 'wss' : 'ws'),
|
||||
route = [proto, "://", $window.location.host, '/api/stream/user?access_token=', token].join('');
|
||||
|
||||
websocket = new WebSocket(route);
|
||||
websocket.onmessage = function (event) {
|
||||
if (callback !== undefined) {
|
||||
callback(angular.fromJson(event.data));
|
||||
}
|
||||
};
|
||||
websocket.onclose = function (event) {
|
||||
console.log('user websocket closed');
|
||||
};
|
||||
};
|
||||
|
||||
this.unsubscribe = function() {
|
||||
callback = undefined;
|
||||
if (websocket !== undefined) {
|
||||
websocket.close();
|
||||
websocket = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.service('feed', FeedService);
|
||||
})();
|
|
@ -1,58 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* The LogService provides access to build
|
||||
* log data using REST API calls.
|
||||
*/
|
||||
function LogService($http, $window) {
|
||||
|
||||
/**
|
||||
* Gets a task logs.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
* @param {number} Number of the build.
|
||||
* @param {number} Number of the task.
|
||||
*/
|
||||
this.get = function(repoName, number, step) {
|
||||
return $http.get('/api/repos/'+repoName+'/logs/'+number+'/'+step);
|
||||
};
|
||||
|
||||
var callback,
|
||||
events,
|
||||
token = localStorage.getItem('access_token');
|
||||
|
||||
this.subscribe = function (repoName, number, step, _callback) {
|
||||
callback = _callback;
|
||||
|
||||
var route = ['/api/stream/', repoName, '/', number, '/', step, '?access_token=', token].join('')
|
||||
events = new EventSource(route, { withCredentials: true });
|
||||
events.onmessage = function (event) {
|
||||
if (callback !== undefined) {
|
||||
callback(event.data);
|
||||
}
|
||||
};
|
||||
events.onerror = function (event) {
|
||||
callback = undefined;
|
||||
if (events !== undefined) {
|
||||
events.close();
|
||||
events = undefined;
|
||||
}
|
||||
console.log('user event stream closed due to error.', event);
|
||||
};
|
||||
};
|
||||
|
||||
this.unsubscribe = function () {
|
||||
callback = undefined;
|
||||
if (events !== undefined) {
|
||||
events.close();
|
||||
events = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.service('logs', LogService);
|
||||
})();
|
|
@ -1,132 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* The RepoService provides access to repository
|
||||
* data using REST API calls.
|
||||
*/
|
||||
function RepoService($http, $window) {
|
||||
|
||||
var callback,
|
||||
websocket,
|
||||
token = localStorage.getItem('access_token');
|
||||
|
||||
/**
|
||||
* Gets a list of all repositories.
|
||||
*/
|
||||
this.list = function () {
|
||||
return $http.get('/api/user/repos');
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a repository by name.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
*/
|
||||
this.get = function (repoName) {
|
||||
return $http.get('/api/repos/' + repoName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new repository.
|
||||
*
|
||||
* @param {object} JSON representation of a repository.
|
||||
*/
|
||||
this.post = function (repoName) {
|
||||
return $http.post('/api/repos/' + repoName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an existing repository.
|
||||
*
|
||||
* @param {object} JSON representation of a repository.
|
||||
*/
|
||||
this.update = function (repo) {
|
||||
return $http.patch('/api/repos/' + repo.full_name, repo);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a repository.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
*/
|
||||
this.delete = function (repoName) {
|
||||
return $http.delete('/api/repos/' + repoName);
|
||||
};
|
||||
|
||||
/**
|
||||
* Watch a repository.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
*/
|
||||
this.watch = function (repoName) {
|
||||
return $http.post('/api/repos/' + repoName + '/watch');
|
||||
};
|
||||
|
||||
/**
|
||||
* Unwatch a repository.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
*/
|
||||
this.unwatch = function (repoName) {
|
||||
return $http.delete('/api/repos/' + repoName + '/unwatch');
|
||||
};
|
||||
|
||||
/**
|
||||
* Encrypt the set of parameters.
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
* @param {string} Plaintext to encrypt.
|
||||
*/
|
||||
this.encrypt = function (repoName, plaintext) {
|
||||
var conf = {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=UTF-8'
|
||||
}
|
||||
}
|
||||
|
||||
return $http.post('/api/repos/' + repoName + '/encrypt', btoa(plaintext), conf);
|
||||
};
|
||||
|
||||
var callback,
|
||||
events,
|
||||
token = localStorage.getItem('access_token');
|
||||
|
||||
/**
|
||||
* Subscribes to a live update feed for a repository
|
||||
*
|
||||
* @param {string} Name of the repository.
|
||||
*/
|
||||
this.subscribe = function (repo, _callback) {
|
||||
callback = _callback;
|
||||
|
||||
events = new EventSource("/api/stream/" + repo + "?access_token=" + token, {withCredentials: true});
|
||||
events.onmessage = function (event) {
|
||||
if (callback !== undefined) {
|
||||
callback(angular.fromJson(event.data));
|
||||
}
|
||||
};
|
||||
events.onerror = function (event) {
|
||||
callback = undefined;
|
||||
if (events !== undefined) {
|
||||
events.close();
|
||||
events = undefined;
|
||||
}
|
||||
console.log('user event stream closed due to error.', event);
|
||||
};
|
||||
};
|
||||
|
||||
this.unsubscribe = function () {
|
||||
callback = undefined;
|
||||
if (events !== undefined) {
|
||||
events.close();
|
||||
events = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.service('repos', RepoService);
|
||||
})();
|
|
@ -1,22 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* The TokenService provides access to user token
|
||||
* data using REST API calls.
|
||||
*/
|
||||
function TokenService($http, $window) {
|
||||
|
||||
/**
|
||||
* Generates a user API token.
|
||||
*/
|
||||
this.post = function(token) {
|
||||
return $http.post('/api/user/token');
|
||||
};
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.service('tokens', TokenService);
|
||||
})();
|
|
@ -1,88 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
(function () {
|
||||
|
||||
/**
|
||||
* Cached user object.
|
||||
*/
|
||||
var _user;
|
||||
|
||||
/**
|
||||
* The UserService provides access to useer
|
||||
* data using REST API calls.
|
||||
*/
|
||||
function UserService($http, $q) {
|
||||
|
||||
/**
|
||||
* Gets a list of all users.
|
||||
*/
|
||||
this.list = function() {
|
||||
return $http.get('/api/users');
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets a user by login.
|
||||
*/
|
||||
this.get = function(login) {
|
||||
return $http.get('/api/users/'+login);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the currently authenticated user.
|
||||
*/
|
||||
this.getCurrent = function() {
|
||||
return $http.get('/api/user');
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an existing user
|
||||
*/
|
||||
this.post = function(user) {
|
||||
return $http.post('/api/users/'+user);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates an existing user
|
||||
*/
|
||||
this.put = function(user) {
|
||||
return $http.patch('/api/users/'+user.login, user);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes a user.
|
||||
*/
|
||||
this.delete = function(user) {
|
||||
return $http.delete('/api/users/'+user.login);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the currently authenticated user from
|
||||
* the local cache. If not exists, it will fetch
|
||||
* from the server.
|
||||
*/
|
||||
this.getCached = function() {
|
||||
var defer = $q.defer();
|
||||
|
||||
// if the user is already authenticated
|
||||
if (_user) {
|
||||
defer.resolve(_user);
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
// else fetch the currently authenticated
|
||||
// user using the REST API.
|
||||
this.getCurrent().then(function(payload){
|
||||
_user=payload;
|
||||
defer.resolve(_user);
|
||||
}).catch(function(){
|
||||
defer.resolve(_user);
|
||||
});
|
||||
|
||||
return defer.promise;
|
||||
}
|
||||
}
|
||||
|
||||
angular
|
||||
.module('drone')
|
||||
.service('users', UserService);
|
||||
})();
|
|
@ -1,390 +0,0 @@
|
|||
var Filter, STYLES, defaults, entities, extend, toHexString, _i, _results,
|
||||
__slice = [].slice;
|
||||
|
||||
STYLES = {
|
||||
'ef0': 'color:#000',
|
||||
'ef1': 'color:#A00',
|
||||
'ef2': 'color:#0A0',
|
||||
'ef3': 'color:#A50',
|
||||
'ef4': 'color:#00A',
|
||||
'ef5': 'color:#A0A',
|
||||
'ef6': 'color:#0AA',
|
||||
'ef7': 'color:#AAA',
|
||||
'ef8': 'color:#555',
|
||||
'ef9': 'color:#F55',
|
||||
'ef10': 'color:#5F5',
|
||||
'ef11': 'color:#FF5',
|
||||
'ef12': 'color:#55F',
|
||||
'ef13': 'color:#F5F',
|
||||
'ef14': 'color:#5FF',
|
||||
'ef15': 'color:#FFF',
|
||||
'eb0': 'background-color:#000',
|
||||
'eb1': 'background-color:#A00',
|
||||
'eb2': 'background-color:#0A0',
|
||||
'eb3': 'background-color:#A50',
|
||||
'eb4': 'background-color:#00A',
|
||||
'eb5': 'background-color:#A0A',
|
||||
'eb6': 'background-color:#0AA',
|
||||
'eb7': 'background-color:#AAA',
|
||||
'eb8': 'background-color:#555',
|
||||
'eb9': 'background-color:#F55',
|
||||
'eb10': 'background-color:#5F5',
|
||||
'eb11': 'background-color:#FF5',
|
||||
'eb12': 'background-color:#55F',
|
||||
'eb13': 'background-color:#F5F',
|
||||
'eb14': 'background-color:#5FF',
|
||||
'eb15': 'background-color:#FFF'
|
||||
};
|
||||
|
||||
toHexString = function(num) {
|
||||
num = num.toString(16);
|
||||
while (num.length < 2) {
|
||||
num = "0" + num;
|
||||
}
|
||||
return num;
|
||||
};
|
||||
|
||||
[0, 1, 2, 3, 4, 5].forEach(function(red) {
|
||||
return [0, 1, 2, 3, 4, 5].forEach(function(green) {
|
||||
return [0, 1, 2, 3, 4, 5].forEach(function(blue) {
|
||||
var b, c, g, n, r, rgb;
|
||||
c = 16 + (red * 36) + (green * 6) + blue;
|
||||
r = red > 0 ? red * 40 + 55 : 0;
|
||||
g = green > 0 ? green * 40 + 55 : 0;
|
||||
b = blue > 0 ? blue * 40 + 55 : 0;
|
||||
rgb = ((function() {
|
||||
var _i, _len, _ref, _results;
|
||||
_ref = [r, g, b];
|
||||
_results = [];
|
||||
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
|
||||
n = _ref[_i];
|
||||
_results.push(toHexString(n));
|
||||
}
|
||||
return _results;
|
||||
})()).join('');
|
||||
STYLES["ef" + c] = "color:#" + rgb;
|
||||
return STYLES["eb" + c] = "background-color:#" + rgb;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
(function() {
|
||||
_results = [];
|
||||
for (_i = 0; _i <= 23; _i++){ _results.push(_i); }
|
||||
return _results;
|
||||
}).apply(this).forEach(function(gray) {
|
||||
var c, l;
|
||||
c = gray + 232;
|
||||
l = toHexString(gray * 10 + 8);
|
||||
STYLES["ef" + c] = "color:#" + l + l + l;
|
||||
return STYLES["eb" + c] = "background-color:#" + l + l + l;
|
||||
});
|
||||
|
||||
extend = function() {
|
||||
var dest, k, obj, objs, v, _j, _len;
|
||||
dest = arguments[0], objs = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
|
||||
for (_j = 0, _len = objs.length; _j < _len; _j++) {
|
||||
obj = objs[_j];
|
||||
for (k in obj) {
|
||||
v = obj[k];
|
||||
dest[k] = v;
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
};
|
||||
|
||||
defaults = {
|
||||
fg: '#FFF',
|
||||
bg: '#000',
|
||||
newline: false,
|
||||
escapeXML: false,
|
||||
stream: false
|
||||
};
|
||||
|
||||
Filter = (function() {
|
||||
function Filter(options) {
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
this.opts = extend({}, defaults, options);
|
||||
this.input = [];
|
||||
this.stack = [];
|
||||
this.stickyStack = [];
|
||||
}
|
||||
|
||||
Filter.prototype.toHtml = function(input) {
|
||||
var buf;
|
||||
this.input = typeof input === 'string' ? [input] : input;
|
||||
buf = [];
|
||||
this.stickyStack.forEach((function(_this) {
|
||||
return function(element) {
|
||||
return _this.generateOutput(element.token, element.data, function(chunk) {
|
||||
return buf.push(chunk);
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
this.forEach(function(chunk) {
|
||||
return buf.push(chunk);
|
||||
});
|
||||
this.input = [];
|
||||
return buf.join('');
|
||||
};
|
||||
|
||||
Filter.prototype.forEach = function(callback) {
|
||||
var buf;
|
||||
buf = '';
|
||||
this.input.forEach((function(_this) {
|
||||
return function(chunk) {
|
||||
buf += chunk;
|
||||
return _this.tokenize(buf, function(token, data) {
|
||||
_this.generateOutput(token, data, callback);
|
||||
if (_this.opts.stream) {
|
||||
return _this.updateStickyStack(token, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
})(this));
|
||||
if (this.stack.length) {
|
||||
return callback(this.resetStyles());
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.generateOutput = function(token, data, callback) {
|
||||
switch (token) {
|
||||
case 'text':
|
||||
return callback(this.pushText(data));
|
||||
case 'display':
|
||||
return this.handleDisplay(data, callback);
|
||||
case 'xterm256':
|
||||
return callback(this.pushStyle("ef" + data));
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.updateStickyStack = function(token, data) {
|
||||
var notCategory;
|
||||
notCategory = function(category) {
|
||||
return function(e) {
|
||||
return (category === null || e.category !== category) && category !== 'all';
|
||||
};
|
||||
};
|
||||
if (token !== 'text') {
|
||||
this.stickyStack = this.stickyStack.filter(notCategory(this.categoryForCode(data)));
|
||||
return this.stickyStack.push({
|
||||
token: token,
|
||||
data: data,
|
||||
category: this.categoryForCode(data)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.handleDisplay = function(code, callback) {
|
||||
code = parseInt(code, 10);
|
||||
if (code === -1) {
|
||||
callback('<br/>');
|
||||
}
|
||||
if (code === 0) {
|
||||
if (this.stack.length) {
|
||||
callback(this.resetStyles());
|
||||
}
|
||||
}
|
||||
if (code === 1) {
|
||||
callback(this.pushTag('b'));
|
||||
}
|
||||
if (code === 2) {
|
||||
|
||||
}
|
||||
if ((2 < code && code < 5)) {
|
||||
callback(this.pushTag('u'));
|
||||
}
|
||||
if ((4 < code && code < 7)) {
|
||||
callback(this.pushTag('blink'));
|
||||
}
|
||||
if (code === 7) {
|
||||
|
||||
}
|
||||
if (code === 8) {
|
||||
callback(this.pushStyle('display:none'));
|
||||
}
|
||||
if (code === 9) {
|
||||
callback(this.pushTag('strike'));
|
||||
}
|
||||
if (code === 24) {
|
||||
callback(this.closeTag('u'));
|
||||
}
|
||||
if ((29 < code && code < 38)) {
|
||||
callback(this.pushStyle("ef" + (code - 30)));
|
||||
}
|
||||
if (code === 39) {
|
||||
callback(this.pushStyle("color:" + this.opts.fg));
|
||||
}
|
||||
if ((39 < code && code < 48)) {
|
||||
callback(this.pushStyle("eb" + (code - 40)));
|
||||
}
|
||||
if (code === 49) {
|
||||
callback(this.pushStyle("background-color:" + this.opts.bg));
|
||||
}
|
||||
if ((89 < code && code < 98)) {
|
||||
callback(this.pushStyle("ef" + (8 + (code - 90))));
|
||||
}
|
||||
if ((99 < code && code < 108)) {
|
||||
return callback(this.pushStyle("eb" + (8 + (code - 100))));
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.categoryForCode = function(code) {
|
||||
code = parseInt(code, 10);
|
||||
if (code === 0) {
|
||||
return 'all';
|
||||
} else if (code === 1) {
|
||||
return 'bold';
|
||||
} else if ((2 < code && code < 5)) {
|
||||
return 'underline';
|
||||
} else if ((4 < code && code < 7)) {
|
||||
return 'blink';
|
||||
} else if (code === 8) {
|
||||
return 'hide';
|
||||
} else if (code === 9) {
|
||||
return 'strike';
|
||||
} else if ((29 < code && code < 38) || code === 39 || (89 < code && code < 98)) {
|
||||
return 'foreground-color';
|
||||
} else if ((39 < code && code < 48) || code === 49 || (99 < code && code < 108)) {
|
||||
return 'background-color';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.pushTag = function(tag, style) {
|
||||
if (style == null) {
|
||||
style = '';
|
||||
}
|
||||
if (style.length && style.indexOf(':') === -1) {
|
||||
style = STYLES[style];
|
||||
}
|
||||
this.stack.push(tag);
|
||||
return ["<" + tag, (style ? " style=\"" + style + "\"" : void 0), ">"].join('');
|
||||
};
|
||||
|
||||
Filter.prototype.pushText = function(text) {
|
||||
if (this.opts.escapeXML) {
|
||||
return entities.encodeXML(text);
|
||||
} else {
|
||||
return text;
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.pushStyle = function(style) {
|
||||
return this.pushTag("span", style);
|
||||
};
|
||||
|
||||
Filter.prototype.closeTag = function(style) {
|
||||
var last;
|
||||
if (this.stack.slice(-1)[0] === style) {
|
||||
last = this.stack.pop();
|
||||
}
|
||||
if (last != null) {
|
||||
return "</" + style + ">";
|
||||
}
|
||||
};
|
||||
|
||||
Filter.prototype.resetStyles = function() {
|
||||
var stack, _ref;
|
||||
_ref = [this.stack, []], stack = _ref[0], this.stack = _ref[1];
|
||||
return stack.reverse().map(function(tag) {
|
||||
return "</" + tag + ">";
|
||||
}).join('');
|
||||
};
|
||||
|
||||
Filter.prototype.tokenize = function(text, callback) {
|
||||
var ansiHandler, ansiMatch, ansiMess, handler, i, length, newline, process, realText, remove, removeXterm256, tokens, _j, _len, _results1;
|
||||
ansiMatch = false;
|
||||
ansiHandler = 3;
|
||||
remove = function(m) {
|
||||
return '';
|
||||
};
|
||||
removeXterm256 = function(m, g1) {
|
||||
callback('xterm256', g1);
|
||||
return '';
|
||||
};
|
||||
newline = (function(_this) {
|
||||
return function(m) {
|
||||
if (_this.opts.newline) {
|
||||
callback('display', -1);
|
||||
} else {
|
||||
callback('text', m);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
})(this);
|
||||
ansiMess = function(m, g1) {
|
||||
var code, _j, _len;
|
||||
ansiMatch = true;
|
||||
if (g1.trim().length === 0) {
|
||||
g1 = '0';
|
||||
}
|
||||
g1 = g1.trimRight(';').split(';');
|
||||
for (_j = 0, _len = g1.length; _j < _len; _j++) {
|
||||
code = g1[_j];
|
||||
callback('display', code);
|
||||
}
|
||||
return '';
|
||||
};
|
||||
realText = function(m) {
|
||||
callback('text', m);
|
||||
return '';
|
||||
};
|
||||
tokens = [
|
||||
{
|
||||
pattern: /^\x08+/,
|
||||
sub: remove
|
||||
}, {
|
||||
pattern: /^\x1b\[[012]?K/,
|
||||
sub: remove
|
||||
}, {
|
||||
pattern: /^\x1b\[38;5;(\d+)m/,
|
||||
sub: removeXterm256
|
||||
}, {
|
||||
pattern: /^\n+/,
|
||||
sub: newline
|
||||
}, {
|
||||
pattern: /^\x1b\[((?:\d{1,3};?)+|)m/,
|
||||
sub: ansiMess
|
||||
}, {
|
||||
pattern: /^\x1b\[?[\d;]{0,3}/,
|
||||
sub: remove
|
||||
}, {
|
||||
pattern: /^([^\x1b\x08\n]+)/,
|
||||
sub: realText
|
||||
}
|
||||
];
|
||||
process = function(handler, i) {
|
||||
var matches;
|
||||
if (i > ansiHandler && ansiMatch) {
|
||||
return;
|
||||
} else {
|
||||
ansiMatch = false;
|
||||
}
|
||||
matches = text.match(handler.pattern);
|
||||
text = text.replace(handler.pattern, handler.sub);
|
||||
if (matches == null) {
|
||||
|
||||
}
|
||||
};
|
||||
_results1 = [];
|
||||
while ((length = text.length) > 0) {
|
||||
for (i = _j = 0, _len = tokens.length; _j < _len; i = ++_j) {
|
||||
handler = tokens[i];
|
||||
process(handler, i);
|
||||
}
|
||||
if (text.length === length) {
|
||||
break;
|
||||
} else {
|
||||
_results1.push(void 0);
|
||||
}
|
||||
}
|
||||
return _results1;
|
||||
};
|
||||
|
||||
return Filter;
|
||||
|
||||
})();
|
|
@ -1,45 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
|
||||
<section class="search">
|
||||
<input type="search" spellcheck="false" placeholder="Filter commits for {{ repo.full_name}}" spellcheck="false" ng-model="search_text"/>
|
||||
<menu>
|
||||
<button type="button" ng-click="unwatch(repo)" ng-if="repo.starred && user">
|
||||
<i class="material-icons">visibility</i>
|
||||
</button>
|
||||
<button type="button" ng-click="watch(repo)" ng-if="!repo.starred && user">
|
||||
<i class="material-icons">visibility_off</i>
|
||||
</button>
|
||||
<a href="/{{ repo.full_name }}/edit" ng-if="repo.permissions.push">
|
||||
<i class="material-icons">tune</i>
|
||||
</a>
|
||||
</menu>
|
||||
</section>
|
||||
|
||||
<div class="blankslate" ng-show="!builds.length && !loading && !error">
|
||||
<i class="material-icons">settings_ethernet</i>
|
||||
<span>Push code to execute your first build.<br/>Be sure to configure you build using the <em>.drone.yml</em> file.</span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-error" ng-show="!!error">
|
||||
<i class="material-icons">error_outline</i>
|
||||
<span>There was an error fetching the build history.</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="list">
|
||||
<a ng-repeat="build in builds | orderBy:'-number' | filter: search_text" ng-href="/{{ repo.full_name }}/{{ build.number}}">
|
||||
<div class="column-status">
|
||||
<div class="status {{ build.status }}">
|
||||
<i class="material-icons">{{ build.status | icon }}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column-fill">
|
||||
<h2>{{ build.head_commit.message }}</h2>
|
||||
<p><em>{{ build.head_commit.author.login }}</em> pushed to <em>{{ build.head_commit.branch }}</em> {{ build.started_at | fromNow }}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</main>
|
|
@ -1,8 +0,0 @@
|
|||
<ol>
|
||||
<li>
|
||||
<a href="/">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>{{owner}} / {{name}}</li>
|
||||
</ol>
|
|
@ -1,59 +0,0 @@
|
|||
<!-- <button ng-if="build.status !== 'pending' && build.status !== 'running'" ng-click="restart()">Restart</button>
|
||||
<button ng-if="build.status === 'pending' || build.status === 'running'" ng-click="cancel()">Cancel</button>
|
||||
-->
|
||||
<main class="flex">
|
||||
<aside>
|
||||
<div>
|
||||
<div class="build-section">
|
||||
<div class="status"><i class="material-icons">close</i></div><!--remove-->
|
||||
<div class="build-summary">
|
||||
<h2><small class="status {{build.status}}">{{ build.status }}</small>{{ build.head_commit.message }}</h2>
|
||||
<p><em>{{ build.head_commit.author.login }}</em> pushed to <em>{{ build.head_commit.branch }}</em> {{ build.started_at | fromNow }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="repo && repo.permissions && repo.permissions.push">
|
||||
<button ng-if="build.status !== 'pending' && build.status !== 'running'" ng-click="restart()" class="button button-restart">Restart</button>
|
||||
<button ng-if="build.status === 'running' && build.jobs.length === 1" ng-click="cancel()" class="button button-cancel">Cancel</button>
|
||||
</div>
|
||||
|
||||
<div ng-class="{'list':true, 'job-list':true, 'matrix-list': build.jobs && build.jobs.length > 1 }">
|
||||
<a ng-repeat="job in build.jobs" ng-href="{{ repo.full_name }}/{{ build.number }}/{{ job.number }}" ng-class="{'active': job.number == step }">
|
||||
<div>
|
||||
<div class="status {{ job.status }} status-small">
|
||||
<i class="material-icons">{{ job.status | icon }}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div>{{ job.number }}</div>
|
||||
<div>
|
||||
<div class="param" ng-repeat="(key, value) in job.environment">
|
||||
{{ key.toUpperCase() }}={{ value }}
|
||||
</div>
|
||||
<div class="meta-group" ng-if="job.status !== 'pending' && job.status !== 'running'">
|
||||
<div class="meta">finished {{ job.started_at | fromNow }}</div>
|
||||
<div class="meta">duration of {{ job.finished_at - job.started_at | toDuration }}</div>
|
||||
<div class="meta">with exit code {{ job.exit_code }}</div>
|
||||
</div>
|
||||
<div class="meta-group" ng-if="job.status === 'running'">
|
||||
<div class="meta">started {{ job.started_at | fromNow }}</div>
|
||||
</div>
|
||||
<div class="meta-group" ng-if="job.status === 'pending'">
|
||||
<div class="meta">pending execution</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<article class="console">
|
||||
|
||||
<div class="button-tail" ng-if="build.status === 'running'" ng-click="tail()">
|
||||
<i class="material-icons">expand_more</i>
|
||||
</div>
|
||||
|
||||
<pre id="term"></pre>
|
||||
</article>
|
||||
</main>
|
|
@ -1,10 +0,0 @@
|
|||
<ol>
|
||||
<li>
|
||||
<a ng-href="/{{ full_name }}">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>{{owner}} / {{name}}</li>
|
||||
<li><i class="material-icons">chevron_right</i></li>
|
||||
<li>{{number}}</li>
|
||||
</ol>
|
|
@ -1,37 +0,0 @@
|
|||
<article>
|
||||
|
||||
<section class="commit-section">
|
||||
<div class="row build-row">
|
||||
<div>
|
||||
<div ng-class="[ 'build-num', task.status ]" ng-if="task"></div>
|
||||
<div ng-class="[ 'build-num', build.status ]" ng-if="!task"></div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>{{ build.head_commit.message }}</h3>
|
||||
|
||||
<p><strong>{{ build.head_commit.author.login }}</strong> pushed to <strong>{{ build.head_commit.branch}}</strong> {{ build.started_at | fromNow }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row build-row sub-build-row" ng-if="build.jobs.length > 1">
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h3 style="margin-top:0px">
|
||||
<div ng-repeat="(key, value) in task.environment">
|
||||
{{ key.toUpperCase() }}={{ value }}
|
||||
</div>
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<pre id="term" ng-if="task && task.status !== 'pending'"></pre>
|
||||
<button class="fab" ng-if="build.status === 'running'" ng-click="tail()"></button>
|
||||
</article>
|
||||
|
||||
|
||||
<button ng-if="build.status !== 'pending' && build.status !== 'running'" ng-click="restart()">Restart</button>
|
||||
<button ng-if="build.status === 'pending' || build.status === 'running'" ng-click="cancel()">Cancel</button>
|
|
@ -1,22 +0,0 @@
|
|||
<div class="breadcrumb" style="position:relative;top:0px;">
|
||||
<a ng-href="/{{ repo.full_name }}/{{ build.sequence }}" class="icon icon-home">
|
||||
<i class="material-icons md-18">home</i>
|
||||
</a>
|
||||
<a ng-href="/{{ repo.full_name }}">{{ repo.owner }} / {{ repo.name }}</a>
|
||||
<span class="spacer"> </span>
|
||||
<a ng-href="/{{ repo.full_name }}/{{ build.number }}">Build # {{ build.number }}</a>
|
||||
<span class="spacer"></span>
|
||||
<a href="#">Job #{{ task.number }}</a>
|
||||
</div>
|
||||
|
||||
<div class="menu">
|
||||
<a ng-href="/{{ repo.full_name }}/edit" class="nav-item settings float-right" ng-if="repo.permissions.pull">
|
||||
<i class="material-icons md-18">edit</i>
|
||||
</a>
|
||||
<button ng-click="watch(repo)" ng-if="!repo.starred && user" class="nav-item star float-right">
|
||||
<i class="material-icons md-18">star_border</i>
|
||||
</button>
|
||||
<button ng-click="unwatch(repo)" ng-if="repo.starred && user" class="nav-item unstar float-right">
|
||||
<i class="material-icons md-18">star</i>
|
||||
</button>
|
||||
</div>
|
|
@ -1,75 +0,0 @@
|
|||
<header>
|
||||
<div class="toolbar" ui-view="toolbar"></div>
|
||||
|
||||
<ul ng-if="user">
|
||||
<li>
|
||||
<img ng-src="{{ user.avatar_url }}" />
|
||||
</li>
|
||||
<li ng-init="show=false">
|
||||
<a href="#" ng-click="show=true">
|
||||
<span>{{user.login}}</span>
|
||||
<i class="material-icons">more_vert</i>
|
||||
</a>
|
||||
<div class="backdrop" ng-show="show" ng-click="show=false"></div>
|
||||
<div class="dropdown" ng-show="show" ng-click="show=false">
|
||||
<ul>
|
||||
<li><a href="/profile">Profile</a></li>
|
||||
<li><a href="/users">Users</a></li>
|
||||
<li><a href="/logout">Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul ng-if="!user">
|
||||
<li>
|
||||
<a href="/login" class="button button-login">Login</a>
|
||||
</li>
|
||||
</ul>
|
||||
</header>
|
||||
|
||||
<div ui-view="content"></div>
|
||||
|
||||
<style>
|
||||
header ul li a i {
|
||||
cursor:pointer;
|
||||
}
|
||||
header li {
|
||||
position:relative;
|
||||
}
|
||||
.backdrop {
|
||||
position:fixed;
|
||||
top:0px;
|
||||
left:0px;
|
||||
right:0px;
|
||||
bottom:0px;
|
||||
z-index:99;
|
||||
cursor:default;
|
||||
}
|
||||
.dropdown {
|
||||
box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 15px;
|
||||
width: 200px;
|
||||
background: #FFF;
|
||||
z-index:999;
|
||||
padding: 10px 0px;
|
||||
}
|
||||
.dropdown ul {
|
||||
float:inherit;
|
||||
margin:0px;
|
||||
}
|
||||
.dropdown li {
|
||||
display:block;
|
||||
}
|
||||
.dropdown li > a {
|
||||
padding: 15px 20px;
|
||||
display: block;
|
||||
line-height: inherit;
|
||||
text-transform: none;
|
||||
}
|
||||
.dropdown li > a:hover {
|
||||
background:#F5F7F9;
|
||||
}
|
||||
</style>
|
|
@ -1,65 +0,0 @@
|
|||
<style>
|
||||
|
||||
|
||||
a.box {
|
||||
background: #FFF;
|
||||
width: 250px;
|
||||
padding: 20px;
|
||||
box-shadow: 0px 0px 0 rgba(255, 255, 255, 0.05);
|
||||
-webkit-transition: all .5s;
|
||||
-webkit-box-flex: 0;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
a.box:hover {
|
||||
box-shadow: 7px 7px 0 rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
div.login-logo {
|
||||
background: url(/static/images/logo.svg) no-repeat center center;
|
||||
background-size: 72px;
|
||||
height: 150px;
|
||||
-webkit-transition: all .5s;
|
||||
}
|
||||
|
||||
div.login {
|
||||
text-align: center;
|
||||
padding: 15px;
|
||||
color: #424242;
|
||||
font-size: 18px;
|
||||
text-transform: uppercase;
|
||||
border-radius: 3px;
|
||||
-webkit-transition: all .5s;
|
||||
background: #424242;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
body > div {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: -webkit-box;
|
||||
display: -webkit-flex;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: center;
|
||||
-webkit-justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a href="/authorize" target="_self" class="box">
|
||||
<div class="login-logo"></div>
|
||||
<div ng-switch="error" class="alert alert-error" ng-if="error">
|
||||
<div ng-switch-when="internal_error">Oops. There was an unexpected error. Please try again.</div>
|
||||
<div ng-switch-when="user_not_found">There was an error authorizing your account.</div>
|
||||
<div ng-switch-when="access_denied_org">Login is restricted to approved organization members only</div>
|
||||
<div ng-switch-when="access_denied">Self-registration is disabled. Please contact the system admin to grant
|
||||
access.
|
||||
</div>
|
||||
</div>
|
||||
<div class="login">Login</div>
|
||||
</a>
|
|
@ -1,20 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
<section>
|
||||
<div class="row">
|
||||
<div>Login</div>
|
||||
<div>{{ user.login }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Email</div>
|
||||
<div>{{ user.email }}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Token</div>
|
||||
<div ng-if="!token" style="color: #1E88E5;text-decoration: underline;cursor: pointer;" ng-click="showToken()">Click to Display Token</div>
|
||||
<div ng-if="token">{{ token }}</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
</main>
|
|
@ -1,8 +0,0 @@
|
|||
<ol>
|
||||
<li>
|
||||
<a href="/">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>Profile</li>
|
||||
</ol>
|
|
@ -1,20 +0,0 @@
|
|||
<article>
|
||||
<section style="background:transparent !important;">
|
||||
<p style="color:#9E9E9E;font-size:15px;line-height:22px;">Register your repository with Drone to enable automated testing. Note that Drone
|
||||
will attempt to add a post-commit hook to your repository. This may require administrative access.</p>
|
||||
</section>
|
||||
<div class="alert alert-error" ng-if="error !== undefined">
|
||||
There was an error adding your repository. Please ensure the
|
||||
repository exists and you have admin privileges.
|
||||
</div>
|
||||
<section>
|
||||
<div style="padding:30px;">
|
||||
<input type="text" placeholder="octocat/Hello-World" ng-model="slug" style="font-size:14px;padding:10px 20px;width:400px;border: 1px solid #d9d9d9;outline:none;" />
|
||||
<div style="margin-top:20px;">
|
||||
<a href="/" style="display: inline-block;font-size:14px; padding:10px 20px;text-transform:uppercase;background:#EEE;text-decoration:none;color:#616161;">Cancel</a>
|
||||
<button ng-click="add(slug)" style="display: inline-block;background:#EEE;font-size:14px; padding:10px 20px;text-transform:uppercase;cursor:pointer;color:#616161;">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</article>
|
|
@ -1,6 +0,0 @@
|
|||
<div class="breadcrumb" style="position:relative;top:0px;">
|
||||
<a href="/" class="icon icon-home">
|
||||
<i class="material-icons md-18">home</i>
|
||||
</a>
|
||||
<a href="#">Add Repository</a>
|
||||
</div>
|
|
@ -1,19 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
|
||||
|
||||
<section style="padding:30px; background-color:#bf616a;">
|
||||
<button ng-click="delete(repo.full_name)" style="color:rgba(255,255,255,0.9);
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
border: 1px solid rgba(255,255,255,0.9);
|
||||
padding: 10px;
|
||||
background:transparent;
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
margin-right: 10px;">Delete</button>
|
||||
<span style="color:rgba(255,255,255,0.9);font-size:16px;">Warning: this action cannot be undone.</span>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
</main>
|
|
@ -1,65 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
<section style="margin-top:20px;">
|
||||
|
||||
<div class="row">
|
||||
<div>Post Commit Hooks</div>
|
||||
<div>
|
||||
<input id="post_commits" type="checkbox" hidden="hidden" ng-model="repo.hooks.push" ng-change="save(repo)"/>
|
||||
<label for="post_commits" class="switch"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Pull Request Hooks</div>
|
||||
<div>
|
||||
<input id="pull_requests" type="checkbox" hidden="hidden" ng-model="repo.hooks.pull_request"
|
||||
ng-change="save(repo)"/>
|
||||
<label for="pull_requests" class="switch"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-if="user.admin">
|
||||
<div>Trusted Code</div>
|
||||
<div>
|
||||
<input id="trusted" type="checkbox" hidden="hidden" ng-model="repo.trusted" ng-change="save(repo)"/>
|
||||
<label for="trusted" class="switch"></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-if="user.admin">
|
||||
<div>Timeout in minutes</div>
|
||||
<div>
|
||||
<input type="range" ng-model="repo.timeout" min="0" max="900" ng-blur="save(repo)"/>
|
||||
<span class="slider-label">{{ repo.timeout }} minutes</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Badge</div>
|
||||
<div>
|
||||
<pre class="snippet">[![Build Status]({{ window.location.origin }}/api/badges/{{ repo.full_name }}/status.svg)]({{ window.location.origin }}/{{ repo.full_name }})</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>CCMenu</div>
|
||||
<div>
|
||||
<pre class="snippet">{{ window.location.origin }}/api/badges/{{ repo.full_name }}/cc.xml</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div>Public Key</div>
|
||||
<div>
|
||||
<pre class="snippet">{{ repo.keypair.public }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<a class="row" ng-href="{{ repo.full_name }}/secure">
|
||||
<div>Secrets</div>
|
||||
<div>
|
||||
Inject secret variables into your build environment
|
||||
</div>
|
||||
</a>
|
||||
<a class="row" ng-href="{{ repo.full_name }}/delete">
|
||||
<div>Delete</div>
|
||||
<div>Delete this repository and its build history</div>
|
||||
</a>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
</main>
|
|
@ -1,23 +0,0 @@
|
|||
<article>
|
||||
<section style="padding:30px;">
|
||||
<h2 style=" padding-left: 0px;
|
||||
padding-top: 0px;
|
||||
margin-bottom: 10px;">Private variables</h2>
|
||||
<div ng-repeat="(key, value) in repo.params" class="row-env">
|
||||
<div><span>export</span> {{ key }} <span>=</span></div>
|
||||
<div>{{ value }}</div>
|
||||
<button ng-click="deleteParam(key)" class="btn-remove"></button>
|
||||
</div>
|
||||
<form style="padding-top:30px;font-family:'Droid Sans Mono','Roboto','Arial';font-size:14px;">
|
||||
<span style="color:#2196F3">export</span>
|
||||
<input type="text" placeholder="FOO" ng-model="param.key" style="border:none;border-bottom:1px solid #9E9E9E;line-height:24px;font-family:'Droid Sans Mono','Roboto','Arial';font-size:14px;" />
|
||||
<span style="color:#2196F3">=</span>
|
||||
<input type="text" placeholder="BAR" ng-model="param.value" style="border:none;border-bottom:1px solid #9E9E9E;line-height:24px;font-family:'Droid Sans Mono','Roboto','Arial';font-size:14px;" />
|
||||
<button ng-click="addParam(param)" style="background: #66bb6a;
|
||||
padding: 8px 20px;
|
||||
color: #FFF;
|
||||
font-family: Roboto;
|
||||
text-transform: uppercase;">add</button>
|
||||
</form>
|
||||
</section>
|
||||
</article>
|
|
@ -1,38 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
|
||||
<section class="search">
|
||||
<input type="search" spellcheck="false" placeholder="Create or find a repository (e.g. octocat/Hello-World)" ng-model="search_text" ng-keypress="add($event, search_text)"/>
|
||||
</section>
|
||||
|
||||
<div class="blankslate" ng-show="!repos.length && !loading && !search_text && !error">
|
||||
<i class="material-icons">control_point_duplicate</i>
|
||||
<span>Get started by adding your repository.<br/>Just type the repository name in the text box above.</span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-create-not-found" ng-show="!!search_text">
|
||||
<i class="material-icons" ng-show="!waiting">control_point_duplicate</i>
|
||||
<i class="material-icons waiting" ng-show="waiting">refresh</i>
|
||||
<span ng-show="!waiting">Press <enter> to add <em>{{search_text}}</em></span>
|
||||
<span ng-show="waiting">Configuring repository <em>{{search_text}}</em></span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-error" ng-show="!!error">
|
||||
<i class="material-icons">error_outline</i>
|
||||
<span>There was an error adding your repository.<br/>Please ensure the
|
||||
repository exists and you have admin privileges.</span>
|
||||
</div>
|
||||
|
||||
<ul class="list cozy" ng-show="!waiting && !error">
|
||||
<a class="row row-repo" ng-repeat="repo in repos | orderBy:'full_name' | filter: search_text" ng-href="/{{ repo.full_name }}">
|
||||
<div class="column-avatar">
|
||||
<img ng-src="{{ repo.avatar_url }}" />
|
||||
</div>
|
||||
<div class="column-fill">
|
||||
<h2>{{ repo.name }}</h2>
|
||||
</div>
|
||||
</a>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
</main>
|
|
@ -1 +0,0 @@
|
|||
<a href="/" class="logo"></a>
|
|
@ -1,28 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
|
||||
<p style=" font-size: 16px;
|
||||
margin-bottom: 10px;">Encrypt and store secret variables in the <code>.drone.sec</code> file<p>
|
||||
<textarea spellcheck="false" placeholder="Enter a plaintext value here" ng-model="plaintext" style="
|
||||
background-color: #eff1f5;
|
||||
border-radius:3px;
|
||||
border:none;
|
||||
padding:10px;
|
||||
min-height:100px;
|
||||
max-width:100%;
|
||||
display:block;
|
||||
min-width:100%;
|
||||
white-space: normal;
|
||||
font-size: 14px;
|
||||
word-wrap: normal;
|
||||
word-break: break-all;"></textarea>
|
||||
<button ng-click="encrypt(plaintext)">Encrypt</button>
|
||||
|
||||
|
||||
<pre ng-if="secure" style=" white-space: normal;
|
||||
font-size: 16px;
|
||||
word-wrap: normal;
|
||||
word-break: break-all;">{{secure}}</pre>
|
||||
|
||||
</article>
|
||||
</main>
|
|
@ -1,8 +0,0 @@
|
|||
<ol>
|
||||
<li>
|
||||
<a ng-href="/{{ full_name}}">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>{{owner}} / <a ng-href="/{{ full_name}}">{{name}}</a></li>
|
||||
</ol>
|
|
@ -1,46 +0,0 @@
|
|||
<main>
|
||||
<article>
|
||||
|
||||
<section class="search">
|
||||
<input type="search" spellcheck="false" placeholder="Create or find a user" ng-model="search_text" ng-keypress="add($event, search_text)" />
|
||||
</section>
|
||||
|
||||
<div class="blankslate" ng-show="users.length === 1 && !search_text && !waiting">
|
||||
<i class="material-icons">control_point_duplicate</i>
|
||||
<span>Get started by adding your team members.<br/>Just type the user login (ie octocat) in the text box above.</span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-create-not-found" ng-show="!!search_text">
|
||||
<i class="material-icons" ng-show="!waiting">control_point_duplicate</i>
|
||||
<i class="material-icons waiting" ng-show="!!waiting">sync</i>
|
||||
<span>Press <enter> to add <em>{{search_text}}</em></span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success" ng-show="new_user">
|
||||
<span>Successfully added user account <em>{{new_user.login}}</em>.</span>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-error" ng-show="!!error">
|
||||
<i class="material-icons">error_outline</i>
|
||||
<span>There was an error adding the user account.</span>
|
||||
</div>
|
||||
|
||||
<ul class="list cozy user-list">
|
||||
<li class="row row-user" ng-repeat="user in users | orderBy:'login' | filter: search_text">
|
||||
<div class="column-avatar">
|
||||
<img ng-src="{{ user.avatar_url || 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm&f=y' }}" />
|
||||
</div>
|
||||
<div class="column-fill">
|
||||
<h2>{{ user.login }} <small ng-if="user.admin" class="label label-success">Admin</small></h2>
|
||||
|
||||
<menu>
|
||||
<button ng-click="toggle(user)" ng-if="!user.admin" class="button success">Grant Admin</button>
|
||||
<button ng-click="toggle(user)" ng-if="user.admin" class="button danger">Revoke Admin</button>
|
||||
<button ng-click="remove(user)" class="button danger">Delete</button>
|
||||
</menu>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</article>
|
||||
</main>
|
|
@ -1,8 +0,0 @@
|
|||
<ol>
|
||||
<li>
|
||||
<a href="/">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
</li>
|
||||
<li>Users</li>
|
||||
</ol>
|
|
@ -1,89 +0,0 @@
|
|||
.alert {
|
||||
padding: 40px;
|
||||
background-color: #F5F7F9;
|
||||
color: #4f5b66;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-radius:3px;
|
||||
margin-bottom:30px;
|
||||
position:relative;
|
||||
}
|
||||
.alert em {
|
||||
font-weight: bold;
|
||||
color: #2b303b;
|
||||
}
|
||||
.alert i.material-icons {
|
||||
font-size: 42px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.alert-error {
|
||||
background: #bf616a;
|
||||
color: #fff;
|
||||
line-height:24px;
|
||||
}
|
||||
.alert-success {
|
||||
background: #a3be8c;
|
||||
color: #fff;
|
||||
line-height:24px;
|
||||
}
|
||||
.alert-create-not-found {
|
||||
color: rgba(255,255,255,0.95);
|
||||
background:#59abe3;
|
||||
background: #8fa1b3;
|
||||
outline:none;
|
||||
border:none;
|
||||
width:100%;
|
||||
font-family: Roboto;
|
||||
}
|
||||
.alert-success em,
|
||||
.alert-error em,
|
||||
.alert-create-not-found em {
|
||||
font-weight: bold;
|
||||
color: #fff;
|
||||
font-size: 120%;
|
||||
margin-left:10px;
|
||||
}
|
||||
|
||||
@-webkit-keyframes delayed-rotate {
|
||||
0% {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@-moz-keyframes delayed-rotate {
|
||||
0% {
|
||||
-moz-transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
-moz-transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@keyframes delayed-rotate {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.waiting {
|
||||
-webkit-animation-name: delayed-rotate;
|
||||
-webkit-animation-duration: 1s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
|
||||
-moz-animation-name: delayed-rotate;
|
||||
-moz-animation-duration: 1s;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-moz-animation-timing-function: ease-in-out;
|
||||
|
||||
animation-name: delayed-rotate;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
.blankslate {
|
||||
padding: 40px 20px;
|
||||
background-color: #59abe3;
|
||||
background-color: #8fa1b3;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
line-height:28px;
|
||||
margin-bottom:30px;
|
||||
}
|
||||
.blankslate.clean-background {
|
||||
|
||||
}
|
||||
.blankslate.spacious {
|
||||
|
||||
}
|
||||
.blankslate i.material-icons {
|
||||
font-size: 42px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
@import url(//fonts.googleapis.com/css?family=Roboto:400,300,500,700);
|
||||
@import url(//fonts.googleapis.com/css?family=Roboto+Mono:300,400,500);
|
||||
@import url(//fonts.googleapis.com/icon?family=Material+Icons);
|
|
@ -1,29 +0,0 @@
|
|||
.label {
|
||||
color: #FFF;
|
||||
background:#8fa1b3;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
padding: 4px 8px;
|
||||
vertical-align: middle;
|
||||
border-radius: 2px;
|
||||
margin-left: 15px;
|
||||
cursor:default;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.label-success {
|
||||
color: #a3be8c;
|
||||
border: 1px solid #a3be8c;
|
||||
background: #fff;
|
||||
/*
|
||||
background: rgba(163, 190, 140, 0.25);
|
||||
border: none;
|
||||
font-size: 11px;*/
|
||||
}
|
||||
.label-failure {
|
||||
color: #bf616a;
|
||||
border: 1px solid #bf616a;
|
||||
background: #fff;
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
.list > a,
|
||||
.list > li {
|
||||
display: flex;
|
||||
padding: 30px 0px;
|
||||
border-top: 1px solid #f0f4f7;
|
||||
color:#4c555a;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
}
|
||||
.list > a:first-child,
|
||||
.list > li:first-child {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.list .column-avatar {
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
}
|
||||
.list .column-status {
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
}
|
||||
.list .column-fill {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.comfortable > a,
|
||||
.comfortable > div,
|
||||
.comfortable > li {
|
||||
padding-top:30px;
|
||||
padding-bottom:30px;
|
||||
}
|
||||
|
||||
.cozy > a,
|
||||
.cozy > div,
|
||||
.cozy > li {
|
||||
padding-top:15px;
|
||||
padding-bottom:15px;
|
||||
}
|
||||
|
||||
.compact > a,
|
||||
.compact > div,
|
||||
.compact > li {
|
||||
padding-top:10px;
|
||||
padding-bottom:10px;
|
||||
}
|
||||
|
||||
.list .column-avatar img {
|
||||
width:32px;
|
||||
height:32px;
|
||||
border-radius:4px;
|
||||
margin-left:5px;
|
||||
}
|
||||
.user-list .column-avatar img {
|
||||
width:32px;
|
||||
height:32px;
|
||||
border-radius:50%;
|
||||
margin-left:5px;
|
||||
}
|
||||
.list h2 {
|
||||
line-height:32px;
|
||||
font-size:18px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom:1px;
|
||||
}
|
||||
.list p {
|
||||
color:#a7adba;
|
||||
margin-top:3px;
|
||||
font-size:14px;
|
||||
line-height:20px;
|
||||
}
|
||||
.list em,
|
||||
.list em {
|
||||
color:#65737e;
|
||||
}
|
||||
/*
|
||||
.matrix-list a.active {
|
||||
background-color: #eff1f5;
|
||||
margin-left: -10px;
|
||||
padding-left: 10px;
|
||||
}*/
|
||||
.matrix-list a.active:after {
|
||||
content:"chevron_right";
|
||||
font-family: "Material Icons";
|
||||
line-height:28px;
|
||||
color: #c0c5ce;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
word-wrap: normal;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
margin-right:-5px;
|
||||
}
|
|
@ -1,769 +0,0 @@
|
|||
html, body {
|
||||
background:#fff;
|
||||
color:#4c555a;
|
||||
font-size:14px;
|
||||
font-family:"Roboto";
|
||||
min-height:100vh;
|
||||
}
|
||||
|
||||
body > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/*
|
||||
* Header and Logo
|
||||
*/
|
||||
|
||||
.logo {
|
||||
float: left;
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
opacity: 1;
|
||||
margin: 14px 0px 0px 14px;
|
||||
margin: 14px 0px 0px 18px;
|
||||
background: url(data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8IS0tIENyZWF0ZWQgd2l0aCBJbmtzY2FwZSAoaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvKSAtLT4NCg0KPHN2Zw0KICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIg0KICAgeG1sbnM6Y2M9Imh0dHA6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL25zIyINCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyINCiAgIHhtbG5zOnN2Zz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciDQogICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciDQogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiDQogICB4bWxuczppbmtzY2FwZT0iaHR0cDovL3d3dy5pbmtzY2FwZS5vcmcvbmFtZXNwYWNlcy9pbmtzY2FwZSINCiAgIHdpZHRoPSIzMiINCiAgIGhlaWdodD0iMzIiDQogICBpZD0ic3ZnMiINCiAgIHZlcnNpb249IjEuMSINCiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguMy4xIHI5ODg2Ig0KICAgc29kaXBvZGk6ZG9jbmFtZT0iZHJvbmVfMzIucG5nIj4NCiAgPGRlZnMNCiAgICAgaWQ9ImRlZnM0IiAvPg0KICA8c29kaXBvZGk6bmFtZWR2aWV3DQogICAgIGlkPSJiYXNlIg0KICAgICBwYWdlY29sb3I9IiNmZmZmZmYiDQogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2Ig0KICAgICBib3JkZXJvcGFjaXR5PSIxLjAiDQogICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwLjAiDQogICAgIGlua3NjYXBlOnBhZ2VzaGFkb3c9IjIiDQogICAgIGlua3NjYXBlOnpvb209IjcuOTE5NTk1OSINCiAgICAgaW5rc2NhcGU6Y3g9IjkuNjYyNzY2NCINCiAgICAgaW5rc2NhcGU6Y3k9IjYuMzk3Njg2NCINCiAgICAgaW5rc2NhcGU6ZG9jdW1lbnQtdW5pdHM9InB4Ig0KICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiDQogICAgIHNob3dncmlkPSJ0cnVlIg0KICAgICBpbmtzY2FwZTpzbmFwLWdsb2JhbD0iZmFsc2UiDQogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTI5NSINCiAgICAgaW5rc2NhcGU6d2luZG93LWhlaWdodD0iNzQ0Ig0KICAgICBpbmtzY2FwZTp3aW5kb3cteD0iNjUiDQogICAgIGlua3NjYXBlOndpbmRvdy15PSIyNCINCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSINCiAgICAgZml0LW1hcmdpbi10b3A9IjAiDQogICAgIGZpdC1tYXJnaW4tbGVmdD0iMCINCiAgICAgZml0LW1hcmdpbi1yaWdodD0iMCINCiAgICAgZml0LW1hcmdpbi1ib3R0b209IjAiPg0KICAgIDxpbmtzY2FwZTpncmlkDQogICAgICAgdHlwZT0ieHlncmlkIg0KICAgICAgIGlkPSJncmlkMjk5NiINCiAgICAgICBlbXBzcGFjaW5nPSI1Ig0KICAgICAgIHZpc2libGU9InRydWUiDQogICAgICAgZW5hYmxlZD0idHJ1ZSINCiAgICAgICBzbmFwdmlzaWJsZWdyaWRsaW5lc29ubHk9InRydWUiDQogICAgICAgb3JpZ2lueD0iLTIxLjcyMDc3OXB4Ig0KICAgICAgIG9yaWdpbnk9Ii05OTAuMzcxODhweCIgLz4NCiAgPC9zb2RpcG9kaTpuYW1lZHZpZXc+DQogIDxtZXRhZGF0YQ0KICAgICBpZD0ibWV0YWRhdGE3Ij4NCiAgICA8cmRmOlJERj4NCiAgICAgIDxjYzpXb3JrDQogICAgICAgICByZGY6YWJvdXQ9IiI+DQogICAgICAgIDxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0Pg0KICAgICAgICA8ZGM6dHlwZQ0KICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPg0KICAgICAgICA8ZGM6dGl0bGU+PC9kYzp0aXRsZT4NCiAgICAgIDwvY2M6V29yaz4NCiAgICA8L3JkZjpSREY+DQogIDwvbWV0YWRhdGE+DQogIDxnDQogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIg0KICAgICBpbmtzY2FwZTpncm91cG1vZGU9ImxheWVyIg0KICAgICBpZD0ibGF5ZXIxIg0KICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjEuNzIwNzc5LC0yOS45OTAyODcpIj4NCiAgICA8cGF0aA0KICAgICAgIHNvZGlwb2RpOnR5cGU9ImFyYyINCiAgICAgICBzdHlsZT0iZmlsbDojMjQyNzI5O2ZpbGwtb3BhY2l0eToxO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDowO3N0cm9rZS1saW5lY2FwOmJ1dHQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLW9wYWNpdHk6MTtzdHJva2UtZGFzaGFycmF5Om5vbmUiDQogICAgICAgaWQ9InBhdGgyOTk4Ig0KICAgICAgIHNvZGlwb2RpOmN4PSIxNzIuMTA0NzQiDQogICAgICAgc29kaXBvZGk6Y3k9IjQ1OC4zOTI0OSINCiAgICAgICBzb2RpcG9kaTpyeD0iNS40Mjk1Njk3Ig0KICAgICAgIHNvZGlwb2RpOnJ5PSI1LjA1MDc2MjciDQogICAgICAgZD0ibSAxNzcuNTM0MzEsNDU4LjM5MjQ5IGEgNS40Mjk1Njk3LDUuMDUwNzYyNyAwIDEgMSAtMTAuODU5MTQsMCA1LjQyOTU2OTcsNS4wNTA3NjI3IDAgMSAxIDEwLjg1OTE0LDAgeiINCiAgICAgICB0cmFuc2Zvcm09Im1hdHJpeCgwLjc1NDE4MzA2LDAsMCwwLjgxMDc0NjgxLC05Mi4wNzA0MDEsLTMyMy4wMDQpIiAvPg0KICAgIDxwYXRoDQogICAgICAgc3R5bGU9ImZpbGw6IzI0MjcyOTtmaWxsLW9wYWNpdHk6MTtzdHJva2Utd2lkdGg6MDtzdHJva2UtbWl0ZXJsaW1pdDo0Ig0KICAgICAgIGQ9Im0gMzcuNzI4MDc1LDMyLjkyNjc2MiBjIDcuMTQ4NjU3LDAuMDU1OTkgMTUuMjc2MDY3LDUuMDk1MzgzIDE2LjAwNzI5NSwxNC41OTI2OTggbCAtOS42Nzg4MywwIGMgMCwwIC0xLjI0Njg3MSwtNS4yNDY5MTYgLTYuMzI4NDY1LC01LjIxMTY3OCAtNS4wODE1OTUsMC4wMzUyMiAtNi4zMjg0NjYsNS4yMTE2NzggLTYuMzI4NDY2LDUuMjExNjc4IGwgLTkuNjc4ODMsMCBjIDAuNDcwMjUsLTkuMzI5NDQ3IDguNDYyMDk3LC0xNC42NTE3NzYgMTYuMDA3Mjk2LC0xNC41OTI2OTggeiINCiAgICAgICBpZD0icmVjdDM4MTAiDQogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCINCiAgICAgICBzb2RpcG9kaTpub2RldHlwZXM9InNjY3pjY3MiIC8+DQogICAgPHBhdGgNCiAgICAgICBzdHlsZT0iZmlsbDojMjQyNzI5O2ZpbGwtb3BhY2l0eToxO3N0cm9rZS13aWR0aDowO3N0cm9rZS1taXRlcmxpbWl0OjQiDQogICAgICAgZD0iTSAzNy43OTQ1NTMsNTkuOTkwMjYgQyAzMi40NjQyMDIsNjAuMDA0NDQgMjcuNDg0NjczLDU1Ljk4MjIyMSAyNS40NDM0MDYsNDkuNzUzMDM2IGwgNS45NTYyMDMsMCBjIDAsMCAxLjI4NDg2NSw1LjE4NzcxOSA2LjM2NjQ1OSw1LjE1MjQ4IDUuMDgxNTk0LC0wLjAzNTIyIDYuMjkwNDcyLC01LjE1MjQ4IDYuMjkwNDcyLC01LjE1MjQ4IGwgNS45NTYyMDMsMCBjIC0xLjMyNzc1LDYuNTg5Nzc0IC02Ljg4NzgzOCwxMC4yMjMwMzggLTEyLjIxODE5LDEwLjIzNzIyNCB6Ig0KICAgICAgIGlkPSJyZWN0MzgxMC0xIg0KICAgICAgIGlua3NjYXBlOmNvbm5lY3Rvci1jdXJ2YXR1cmU9IjAiDQogICAgICAgc29kaXBvZGk6bm9kZXR5cGVzPSJzY2N6Y2NzIiAvPg0KICA8L2c+DQo8L3N2Zz4=) no-repeat center center;
|
||||
}
|
||||
header {
|
||||
height:60px;
|
||||
min-height:60px;
|
||||
max-height:60px;
|
||||
/*border-bottom:1px solid #f2f2f2;*/
|
||||
box-sizing: border-box;
|
||||
}
|
||||
header ul {
|
||||
float:right;
|
||||
margin-right:20px;
|
||||
}
|
||||
header ul li span {
|
||||
margin-right:7px;
|
||||
margin-right:3px;
|
||||
}
|
||||
header li {
|
||||
display:inline-block;
|
||||
}
|
||||
header li a {
|
||||
line-height:60px;
|
||||
display:inline-block;
|
||||
vertical-align: middle;
|
||||
padding:0px 10px;
|
||||
text-transform: lowercase;
|
||||
color:#4c555a;
|
||||
text-decoration: none;
|
||||
font-size:15px;
|
||||
}
|
||||
header li i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
header li.active a {
|
||||
color:#0099e5;
|
||||
}
|
||||
|
||||
header li img {
|
||||
border-radius:50%;
|
||||
width:32px;
|
||||
height:32px;
|
||||
vertical-align: middle;
|
||||
margin-right:3px;
|
||||
}
|
||||
|
||||
header ol {
|
||||
float:left;
|
||||
margin-left:15px;
|
||||
}
|
||||
header ol li {
|
||||
line-height:60px;
|
||||
display:inline-block;
|
||||
vertical-align: middle;
|
||||
font-size:21px;
|
||||
}
|
||||
header ol li:first-child {
|
||||
font-size:0px;
|
||||
}
|
||||
header ol li i {
|
||||
vertical-align: middle;
|
||||
font-size:28px;
|
||||
}
|
||||
header ol li a {
|
||||
line-height:inherit;
|
||||
vertical-align: middle;
|
||||
font-size:0px;
|
||||
}
|
||||
header ol li a i.material-icons {
|
||||
vertical-align: middle;
|
||||
font-size:25px;
|
||||
}
|
||||
header .private {
|
||||
color: #ebcb8b;
|
||||
opacity: 0.8;
|
||||
/*color: #000;
|
||||
opacity:0.25;*/
|
||||
padding-left:15px;
|
||||
margin-top:-5px;
|
||||
display:none;
|
||||
}
|
||||
header .private i {
|
||||
font-size:27px;
|
||||
}
|
||||
|
||||
input[type="search"] {
|
||||
font-family:Roboto;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main Seciton / Layout
|
||||
*/
|
||||
|
||||
.flex {
|
||||
display:flex;
|
||||
flex:1 1 auto;
|
||||
margin-top:0px;
|
||||
}
|
||||
main article {
|
||||
margin:0px auto;
|
||||
max-width:900px;
|
||||
padding:30px 40px;
|
||||
}
|
||||
.flex article {
|
||||
padding-bottom:0px;
|
||||
}
|
||||
.flex article pre {
|
||||
min-height: calc(100vh - 120px);
|
||||
margin-bottom:20px;
|
||||
}
|
||||
main aside {
|
||||
min-width:450px;
|
||||
width:450px;
|
||||
box-sizing: border-box;
|
||||
padding:40px 50px;
|
||||
}
|
||||
main aside > div {
|
||||
position: sticky;
|
||||
top:0px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Build Console
|
||||
*/
|
||||
|
||||
.console {
|
||||
margin:0px;
|
||||
flex: 1 1 auto;
|
||||
/*margin-top: -1px;*/
|
||||
max-width:auto;
|
||||
padding:30px 40px 20px 0px;
|
||||
}
|
||||
|
||||
.console pre {
|
||||
color:#eff1f5;
|
||||
border-radius:2px;
|
||||
background:#2b303b;
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
box-sizing: border-box;
|
||||
padding:35px 40px;
|
||||
line-height:18px;
|
||||
font-family: "Roboto Mono";
|
||||
font-size:12px;
|
||||
font-weight:300;
|
||||
}
|
||||
|
||||
@media (-webkit-max-device-pixel-ratio: 1) {
|
||||
.console pre {
|
||||
font-weight:400;
|
||||
line-height:20px;
|
||||
font-size:13px;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Repo List
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Build List
|
||||
*/
|
||||
|
||||
.repo-list { }
|
||||
.repo-list li {
|
||||
display:flex;
|
||||
padding:30px 0px;
|
||||
border-top:1px solid #f0f4f7;
|
||||
position:relative;
|
||||
}
|
||||
.repo-list img {
|
||||
width:32px;
|
||||
height:32px;
|
||||
border-radius:50%;
|
||||
margin-left:5px;
|
||||
}
|
||||
.repo-list li:first-child {
|
||||
border-top:none;
|
||||
}
|
||||
.repo-list li > div:first-child {
|
||||
width:60px;
|
||||
min-width:60px;
|
||||
}
|
||||
.repo-list li > div:nth-child(2) {
|
||||
flex:1 1 auto;
|
||||
}
|
||||
.repo-list h2 {
|
||||
line-height:32px;
|
||||
font-size:18px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom:1px;
|
||||
}
|
||||
.repo-list p {
|
||||
color:#a7adba;
|
||||
margin-top:3px;
|
||||
font-size:14px;
|
||||
line-height:20px;
|
||||
display:none;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Build List
|
||||
*/
|
||||
|
||||
.build-list { }
|
||||
.build-list li {
|
||||
display:flex;
|
||||
/*padding:30px 0px 30px 35px;*/
|
||||
padding:30px 0px;
|
||||
border-top:1px solid #f0f4f7;
|
||||
position:relative;
|
||||
}
|
||||
/*.build-list li:nth-child(even) {
|
||||
background:#FAFBFC;
|
||||
}*/
|
||||
.build-list li:first-child {
|
||||
border-top:none;
|
||||
}
|
||||
.build-list li > div:first-child {
|
||||
width:60px;
|
||||
min-width:60px;
|
||||
}
|
||||
.build-list li > div:nth-child(2) {
|
||||
flex:1 1 auto;
|
||||
}
|
||||
.build-list h2 {
|
||||
line-height:32px;
|
||||
font-size:18px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin-bottom:1px;
|
||||
}
|
||||
.build-section p,
|
||||
.build-list p {
|
||||
color:#a7adba;
|
||||
margin-top:3px;
|
||||
font-size:14px;
|
||||
line-height:20px;
|
||||
}
|
||||
.build-section em,
|
||||
.build-list em {
|
||||
color:#65737e;
|
||||
}
|
||||
.build-list h2 small:before {
|
||||
content:"#";
|
||||
margin-right:3px;
|
||||
}
|
||||
.build-list h2 small:after {
|
||||
content:" ";
|
||||
margin-right:10px;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Build Details
|
||||
*/
|
||||
|
||||
.build-section {
|
||||
margin-bottom:40px;
|
||||
display:flex;
|
||||
}
|
||||
.build-section > div:first-child {
|
||||
min-width:60px;
|
||||
width:60px;
|
||||
display:none;
|
||||
}
|
||||
.build-section > div:last-child {
|
||||
flex: 1 1 auto;
|
||||
padding-top:4px;
|
||||
font-size:17px;
|
||||
line-height:22px;
|
||||
}
|
||||
.build-detail {
|
||||
padding-left:50px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.build-summary h2 {
|
||||
line-height:23px;
|
||||
}
|
||||
.build-summary h2 small {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
padding: 3px 10px;
|
||||
border-radius: 2px;
|
||||
margin-right: 5px;
|
||||
vertical-align: top;
|
||||
color:#FFF;
|
||||
width:auto;
|
||||
height:auto;
|
||||
display:inline;
|
||||
}
|
||||
.build-summary p {
|
||||
margin-top:10px;
|
||||
}
|
||||
|
||||
/*
|
||||
* Job List
|
||||
*/
|
||||
|
||||
.job-list { }
|
||||
.job-list > a,
|
||||
.job-list > li {
|
||||
display:flex;
|
||||
padding:20px 0px;
|
||||
border-top:1px solid #f0f4f7;
|
||||
}
|
||||
|
||||
.job-list > a > div:first-child,
|
||||
.job-list > li > div:first-child {
|
||||
width:50px;
|
||||
min-width:50px;
|
||||
}
|
||||
.job-list > a > div:nth-child(2),
|
||||
.job-list > li > div:nth-child(2) { /** TEMPORARILY HIDDEN. MAY DECIDE TO REMOVE */
|
||||
min-width:25px;
|
||||
font-size:13px;
|
||||
text-align:right;
|
||||
padding: 10px 20px 0px 10px;
|
||||
font-weight:bold;
|
||||
font-size:15px;
|
||||
display:none;
|
||||
}
|
||||
.job-list > a > div:nth-child(3),
|
||||
.job-list > li > div:nth-child(3) {
|
||||
flex:1 1 auto;
|
||||
font-size:13px;
|
||||
}
|
||||
.job-list > a div.param,
|
||||
.job-list > li div.param {
|
||||
margin-top:5px;
|
||||
}
|
||||
.job-list > a div.param:first-child,
|
||||
.job-list > li div.param:first-child {
|
||||
margin-top:9px;
|
||||
}
|
||||
.job-list > a div.meta,
|
||||
.job-list > li div.meta {
|
||||
margin-top: 5px;
|
||||
color: #a7adba;
|
||||
}
|
||||
.job-list > a div.meta-group {
|
||||
padding-top:10px;
|
||||
display:none;
|
||||
}
|
||||
.job-list > a.active div.meta-group {
|
||||
display:block;
|
||||
}
|
||||
.job-list:not(.matrix-list) > a div.meta:first-child {
|
||||
color: #4c555a;
|
||||
margin-top: -2px;
|
||||
padding-bottom: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
li menu {
|
||||
position:absolute;
|
||||
top:15px;
|
||||
right:0px;
|
||||
display:none;
|
||||
}
|
||||
li:hover menu {
|
||||
display:inline-block;
|
||||
}
|
||||
li menu .button {
|
||||
border-radius:50%;
|
||||
width:36px;
|
||||
height:36px;
|
||||
line-height:36px;
|
||||
padding:0px;
|
||||
|
||||
color:#dfe1e8;
|
||||
background: #fff;
|
||||
border:1px solid #FFF;
|
||||
outline:none;
|
||||
cursor:pointer;
|
||||
|
||||
|
||||
width: auto;
|
||||
text-transform: uppercase;
|
||||
padding: 0px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 11px;
|
||||
line-height: 30px;
|
||||
height: auto;
|
||||
margin-left: 10px;
|
||||
|
||||
|
||||
}
|
||||
li menu .button:hover {
|
||||
border:1px solid #a7adba;
|
||||
background:#FFF;
|
||||
color:#a7adba;
|
||||
}
|
||||
li menu .button i {
|
||||
font-size:22px;
|
||||
}
|
||||
|
||||
li menu .button.success {
|
||||
color: #FFF;
|
||||
border:1px solid #a3be8c;
|
||||
background: #a3be8c;
|
||||
|
||||
color: #a3be8c;
|
||||
background:#FFF;
|
||||
|
||||
|
||||
}
|
||||
li menu .button.danger {
|
||||
color: #FFF;
|
||||
background: #bf616a;
|
||||
|
||||
color:#bf616a;
|
||||
background:#FFF;
|
||||
border:1px solid #bf616a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Tail button to follow a build
|
||||
*/
|
||||
|
||||
.button-tail {
|
||||
position: fixed;
|
||||
bottom: 50px;
|
||||
right: 80px;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
border-radius: 50%;
|
||||
box-shadow: 1px 2px 2px rgba(0,0,0,0.2);
|
||||
cursor: pointer;
|
||||
|
||||
bottom: 15px;
|
||||
right: 60px;
|
||||
}
|
||||
.button-tail i {
|
||||
text-align:center;
|
||||
color:rgba(255,255,255,0.5);
|
||||
width:38px;
|
||||
line-height:38px;
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Random buttons used throughtout
|
||||
*/
|
||||
/*
|
||||
menu {
|
||||
display:block;
|
||||
text-align:right;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
menu .button {
|
||||
margin-left:5px;
|
||||
border:1px solid #f0f0f0;
|
||||
}
|
||||
menu .button span {
|
||||
margin:0px 10px;
|
||||
text-transform: uppercase;
|
||||
}*/
|
||||
.button {
|
||||
border-radius:2px;
|
||||
line-height:32px;
|
||||
display:inline-block;
|
||||
vertical-align: middle;
|
||||
padding:4px 15px 4px 15px;
|
||||
text-decoration: none;
|
||||
font-size:13px;
|
||||
background:#eff1f5;
|
||||
color: rgba(0,0,0,0.5);
|
||||
}
|
||||
.button:hover {
|
||||
background:#dfe1e8;
|
||||
}
|
||||
/*
|
||||
.button-watch {
|
||||
background: #a7adba;
|
||||
}
|
||||
.button-settings {
|
||||
background:#d08770;
|
||||
}
|
||||
*/
|
||||
.button i {
|
||||
vertical-align: middle;
|
||||
/*margin-right:5px;*/
|
||||
line-height:17px;
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
.button-restart {
|
||||
background: #FFF;
|
||||
border: 1px solid #8fa1b3;
|
||||
color: #8fa1b3;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
margin-top:-20px;
|
||||
padding: 0px 10px;
|
||||
line-height: 25px;
|
||||
cursor:pointer;
|
||||
}
|
||||
.button-cancel {
|
||||
background: #FFF;
|
||||
border: 1px solid #d08770;
|
||||
color: #d08770;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 20px;
|
||||
margin-top:-20px;
|
||||
padding: 0px 10px;
|
||||
line-height: 25px;
|
||||
cursor:pointer;
|
||||
}
|
||||
.button-cancel:hover {
|
||||
background: #d08770;
|
||||
border: 1px solid #d08770;
|
||||
color: #fff;
|
||||
}
|
||||
.button-login {
|
||||
margin-top: 15px;
|
||||
font-size: 13px;
|
||||
text-transform: uppercase;
|
||||
background: #FFF;
|
||||
border: 1px solid #8fa1b3;
|
||||
color: #8fa1b3;
|
||||
line-height: 25px;
|
||||
}
|
||||
.button-login:hover {
|
||||
background: #8fa1b3;
|
||||
border: 1px solid #8fa1b3;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Status Indicator
|
||||
*/
|
||||
|
||||
.status {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
}
|
||||
.status i {
|
||||
line-height: 32px;
|
||||
width: 32px;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
font-size:24px;
|
||||
color:rgba(255,255,255,0.7);
|
||||
}
|
||||
.status-small {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
.status-small i {
|
||||
line-height: 28px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
/* Search Form */
|
||||
.search {
|
||||
display:block;
|
||||
width:100%;
|
||||
margin:0px 0px 37px 0px;
|
||||
}
|
||||
input[type="search"] {
|
||||
background: #eff1f5;
|
||||
line-height: 50px;
|
||||
border:none;
|
||||
border-radius:3px;
|
||||
display:block;
|
||||
width:100%;
|
||||
font-size:14px;
|
||||
padding:0px 15px;
|
||||
font-family:"Roboto";
|
||||
|
||||
}
|
||||
|
||||
/* SETTINGS SECTION */
|
||||
|
||||
|
||||
section .row {
|
||||
display: flex;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
}
|
||||
section .row a {
|
||||
text-decoration: none;
|
||||
}
|
||||
section .row > div:first-child {
|
||||
padding: 30px 0px;
|
||||
border-bottom: 1px solid #f0f4f7;
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
font-size:15px;
|
||||
color: #343d46;
|
||||
}
|
||||
|
||||
section .row > div:last-child {
|
||||
flex: 1 1 auto;
|
||||
padding: 30px;
|
||||
border-bottom: 1px solid #f0f4f7;
|
||||
overflow:hidden;
|
||||
color: #65737e;
|
||||
}
|
||||
|
||||
section .row:last-child > div {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
pre.snippet-padding {
|
||||
padding: 30px 0px;
|
||||
}
|
||||
pre.snippet {
|
||||
white-space: pre-wrap;
|
||||
word-wrap: break-word;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
font-family: "Roboto Mono";
|
||||
}
|
||||
.slider-label {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
::-webkit-input-placeholder { /* WebKit browsers */
|
||||
color: #a7adba;
|
||||
}
|
||||
:-moz-placeholder { /* Mozilla Firefox 4 to 18 */
|
||||
color: #a7adba;
|
||||
opacity: 1;
|
||||
}
|
||||
::-moz-placeholder { /* Mozilla Firefox 19+ */
|
||||
color: #a7adba;
|
||||
opacity: 1;
|
||||
}
|
||||
:-ms-input-placeholder { /* Internet Explorer 10+ */
|
||||
color: #a7adba;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ROUND INDICATORS */
|
||||
.status {
|
||||
border-radius:50%;
|
||||
}
|
||||
.status.error,
|
||||
.status.killed,
|
||||
.status.failure {
|
||||
background: #bf616a;
|
||||
}
|
||||
.status.success {
|
||||
background:#a3be8c;
|
||||
}
|
||||
.status.running,
|
||||
.status.pending {
|
||||
background:#ebcb8b;
|
||||
}
|
||||
|
||||
.status.running i,
|
||||
.status.pending i {
|
||||
-webkit-animation-name: delayed-rotate;
|
||||
-webkit-animation-duration: 1s;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
-webkit-animation-timing-function: ease-in-out;
|
||||
|
||||
-moz-animation-name: delayed-rotate;
|
||||
-moz-animation-duration: 1s;
|
||||
-moz-animation-iteration-count: infinite;
|
||||
-moz-animation-timing-function: ease-in-out;
|
||||
|
||||
animation-name: delayed-rotate;
|
||||
animation-duration: 1s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: ease-in-out;
|
||||
}
|
||||
.build-list li > div:first-child,
|
||||
.job-list li > div:first-child {
|
||||
width: 55px;
|
||||
min-width: 55px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Search Menu
|
||||
*/
|
||||
|
||||
section.search {
|
||||
background: #eff1f5;
|
||||
margin: 0px 0px 37px 0px;
|
||||
border-radius: 3px;
|
||||
display:flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
section.search input[type="search"] {
|
||||
line-height: 50px;
|
||||
border: none;
|
||||
border-radius: 3px;
|
||||
display: block;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
padding: 0px 15px;
|
||||
flex:1 1 auto;
|
||||
font-family:"Roboto";
|
||||
}
|
||||
section.search menu {
|
||||
display:flex;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
section.search menu button,
|
||||
section.search menu a {
|
||||
line-height: 50px;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 0px 15px;
|
||||
color: rgba(0,0,0,0.3);
|
||||
background:transparent;
|
||||
border:none;
|
||||
border-left: 1px solid #FFF;
|
||||
cursor:pointer;
|
||||
outline:none;
|
||||
}
|
||||
section.search menu i {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Lists
|
||||
*/
|
|
@ -1,109 +0,0 @@
|
|||
input[type="range"]:focus ~ .slider-label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input[type=range] {
|
||||
-webkit-appearance: none;
|
||||
margin: 6px 0;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
input[type=range]:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-runnable-track {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
box-shadow: none;
|
||||
background: rgba(0, 150, 136, 0.5);
|
||||
background: rgba(102, 187, 106, 0.5);
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type=range]::-webkit-slider-thumb {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 50px;
|
||||
background: #009688;
|
||||
background: #66bb6a;
|
||||
cursor: pointer;
|
||||
-webkit-appearance: none;
|
||||
margin-top: -10px;
|
||||
}
|
||||
|
||||
input[type=range]:focus::-webkit-slider-runnable-track {
|
||||
background: rgba(0, 150, 136, 0.5);
|
||||
background: rgba(102, 187, 106, 0.5);
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-track {
|
||||
width: 100%;
|
||||
height: 8px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
box-shadow: none;
|
||||
background: rgba(0, 150, 136, 0.5);
|
||||
background: rgba(102, 187, 106, 0.5);
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type=range]::-moz-range-thumb {
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
border-radius: 50px;
|
||||
background: #009688;
|
||||
background: #66bb6a;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-track {
|
||||
width: 100%;
|
||||
height: 8.4px;
|
||||
cursor: pointer;
|
||||
animate: 0.2s;
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
border-width: 16px 0;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-fill-lower {
|
||||
background: #2a6495;
|
||||
border: 0.2px solid #010101;
|
||||
border-radius: 2.6px;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-fill-upper {
|
||||
background: #3071a9;
|
||||
border: 0.2px solid #010101;
|
||||
border-radius: 2.6px;
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
}
|
||||
|
||||
input[type=range]::-ms-thumb {
|
||||
box-shadow: 1px 1px 1px #000000, 0px 0px 1px #0d0d0d;
|
||||
border: 1px solid #000000;
|
||||
height: 36px;
|
||||
width: 16px;
|
||||
border-radius: 3px;
|
||||
background: #ffffff;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type=range]:focus::-ms-fill-lower {
|
||||
background: #3071a9;
|
||||
}
|
||||
|
||||
input[type=range]:focus::-ms-fill-upper {
|
||||
background: #367ebd;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||
article, aside, canvas, details, embed,
|
||||
figure, figcaption, footer, header, hgroup,
|
||||
menu, nav, output, ruby, section, summary,
|
||||
time, mark, audio, video {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
article, aside, details, figcaption, figure,
|
||||
footer, header, hgroup, menu, nav, section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
textarea, input { outline: none; }
|
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
http://codepen.io/batazor/pen/KwKryj
|
||||
*/
|
||||
|
||||
.switch {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 40px;
|
||||
height: 8px;
|
||||
border-radius: 10.416666666666668px;
|
||||
background: #E0E0E0;
|
||||
-webkit-transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition: background 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -8.604166666666667px;
|
||||
left: -2.604166666666667px;
|
||||
width: 26.04166666666667px;
|
||||
height: 26.04166666666667px;
|
||||
background: #bdbdbd;
|
||||
border-radius: 50%;
|
||||
-webkit-transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition: left 0.28s cubic-bezier(0.4, 0, 0.2, 1), background 0.28s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.switch:active::before {
|
||||
box-shadow: 0 2px 10.416666666666668px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.switch:active::before {
|
||||
box-shadow: 0 2px 10.416666666666668px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
input:checked + .switch {
|
||||
background: rgba(0, 150, 136, 0.5);
|
||||
background: rgba(102, 187, 106, 0.5);
|
||||
}
|
||||
|
||||
input:checked + .switch::before {
|
||||
left: 20.562499999999996px;
|
||||
background: #009688;
|
||||
background: #66bb6a;
|
||||
}
|
||||
|
||||
input:checked + .switch:active::before {
|
||||
box-shadow: 0 2px 10.416666666666668px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 150, 136, 0.2);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package bus
|
||||
|
||||
const (
|
||||
EventRepo = "repo"
|
||||
EventUser = "user"
|
||||
EventAgent = "agent"
|
||||
)
|
||||
|
||||
type Event struct {
|
||||
Kind string
|
||||
Name string
|
||||
Msg []byte
|
||||
}
|
||||
|
||||
type Bus interface {
|
||||
Subscribe(chan *Event)
|
||||
Unsubscribe(chan *Event)
|
||||
Send(*Event)
|
||||
}
|
|
@ -1,471 +0,0 @@
|
|||
// Copyright 2011 The goauth2 Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package oauth supports making OAuth2-authenticated HTTP requests.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// // Specify your configuration. (typically as a global variable)
|
||||
// var config = &oauth.Config{
|
||||
// ClientId: YOUR_CLIENT_ID,
|
||||
// ClientSecret: YOUR_CLIENT_SECRET,
|
||||
// Scope: "https://www.googleapis.com/auth/buzz",
|
||||
// AuthURL: "https://accounts.google.com/o/oauth2/auth",
|
||||
// TokenURL: "https://accounts.google.com/o/oauth2/token",
|
||||
// RedirectURL: "http://you.example.org/handler",
|
||||
// }
|
||||
//
|
||||
// // A landing page redirects to the OAuth provider to get the auth code.
|
||||
// func landing(w http.ResponseWriter, r *http.Request) {
|
||||
// http.Redirect(w, r, config.AuthCodeURL("foo"), http.StatusFound)
|
||||
// }
|
||||
//
|
||||
// // The user will be redirected back to this handler, that takes the
|
||||
// // "code" query parameter and Exchanges it for an access token.
|
||||
// func handler(w http.ResponseWriter, r *http.Request) {
|
||||
// t := &oauth.Transport{Config: config}
|
||||
// t.Exchange(r.FormValue("code"))
|
||||
// // The Transport now has a valid Token. Create an *http.Client
|
||||
// // with which we can make authenticated API requests.
|
||||
// c := t.Client()
|
||||
// c.Post(...)
|
||||
// // ...
|
||||
// // btw, r.FormValue("state") == "foo"
|
||||
// }
|
||||
//
|
||||
package oauth2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OAuthError is the error type returned by many operations.
|
||||
//
|
||||
// In retrospect it should not exist. Don't depend on it.
|
||||
type OAuthError struct {
|
||||
prefix string
|
||||
msg string
|
||||
}
|
||||
|
||||
func (oe OAuthError) Error() string {
|
||||
return "OAuthError: " + oe.prefix + ": " + oe.msg
|
||||
}
|
||||
|
||||
// Cache specifies the methods that implement a Token cache.
|
||||
type Cache interface {
|
||||
Token() (*Token, error)
|
||||
PutToken(*Token) error
|
||||
}
|
||||
|
||||
// CacheFile implements Cache. Its value is the name of the file in which
|
||||
// the Token is stored in JSON format.
|
||||
type CacheFile string
|
||||
|
||||
func (f CacheFile) Token() (*Token, error) {
|
||||
file, err := os.Open(string(f))
|
||||
if err != nil {
|
||||
return nil, OAuthError{"CacheFile.Token", err.Error()}
|
||||
}
|
||||
defer file.Close()
|
||||
tok := &Token{}
|
||||
if err := json.NewDecoder(file).Decode(tok); err != nil {
|
||||
return nil, OAuthError{"CacheFile.Token", err.Error()}
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
func (f CacheFile) PutToken(tok *Token) error {
|
||||
file, err := os.OpenFile(string(f), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
|
||||
if err != nil {
|
||||
return OAuthError{"CacheFile.PutToken", err.Error()}
|
||||
}
|
||||
if err := json.NewEncoder(file).Encode(tok); err != nil {
|
||||
file.Close()
|
||||
return OAuthError{"CacheFile.PutToken", err.Error()}
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return OAuthError{"CacheFile.PutToken", err.Error()}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config is the configuration of an OAuth consumer.
|
||||
type Config struct {
|
||||
// ClientId is the OAuth client identifier used when communicating with
|
||||
// the configured OAuth provider.
|
||||
ClientId string
|
||||
|
||||
// ClientSecret is the OAuth client secret used when communicating with
|
||||
// the configured OAuth provider.
|
||||
ClientSecret string
|
||||
|
||||
// Scope identifies the level of access being requested. Multiple scope
|
||||
// values should be provided as a space-delimited string.
|
||||
Scope string
|
||||
|
||||
// AuthURL is the URL the user will be directed to in order to grant
|
||||
// access.
|
||||
AuthURL string
|
||||
|
||||
// TokenURL is the URL used to retrieve OAuth tokens.
|
||||
TokenURL string
|
||||
|
||||
// RedirectURL is the URL to which the user will be returned after
|
||||
// granting (or denying) access.
|
||||
RedirectURL string
|
||||
|
||||
// TokenCache allows tokens to be cached for subsequent requests.
|
||||
TokenCache Cache
|
||||
|
||||
// AccessType is an OAuth extension that gets sent as the
|
||||
// "access_type" field in the URL from AuthCodeURL.
|
||||
// See https://developers.google.com/accounts/docs/OAuth2WebServer.
|
||||
// It may be "online" (the default) or "offline".
|
||||
// If your application needs to refresh access tokens when the
|
||||
// user is not present at the browser, then use offline. This
|
||||
// will result in your application obtaining a refresh token
|
||||
// the first time your application exchanges an authorization
|
||||
// code for a user.
|
||||
AccessType string
|
||||
|
||||
// ApprovalPrompt indicates whether the user should be
|
||||
// re-prompted for consent. If set to "auto" (default) the
|
||||
// user will be prompted only if they haven't previously
|
||||
// granted consent and the code can only be exchanged for an
|
||||
// access token.
|
||||
// If set to "force" the user will always be prompted, and the
|
||||
// code can be exchanged for a refresh token.
|
||||
ApprovalPrompt string
|
||||
}
|
||||
|
||||
// Token contains an end-user's tokens.
|
||||
// This is the data you must store to persist authentication.
|
||||
type Token struct {
|
||||
AccessToken string
|
||||
RefreshToken string
|
||||
Expiry time.Time // If zero the token has no (known) expiry time.
|
||||
|
||||
// Extra optionally contains extra metadata from the server
|
||||
// when updating a token. The only current key that may be
|
||||
// populated is "id_token". It may be nil and will be
|
||||
// initialized as needed.
|
||||
Extra map[string]string
|
||||
}
|
||||
|
||||
// Expired reports whether the token has expired or is invalid.
|
||||
func (t *Token) Expired() bool {
|
||||
if t.AccessToken == "" {
|
||||
return true
|
||||
}
|
||||
if t.Expiry.IsZero() {
|
||||
return false
|
||||
}
|
||||
return t.Expiry.Before(time.Now())
|
||||
}
|
||||
|
||||
// Transport implements http.RoundTripper. When configured with a valid
|
||||
// Config and Token it can be used to make authenticated HTTP requests.
|
||||
//
|
||||
// t := &oauth.Transport{config}
|
||||
// t.Exchange(code)
|
||||
// // t now contains a valid Token
|
||||
// r, _, err := t.Client().Get("http://example.org/url/requiring/auth")
|
||||
//
|
||||
// It will automatically refresh the Token if it can,
|
||||
// updating the supplied Token in place.
|
||||
type Transport struct {
|
||||
*Config
|
||||
*Token
|
||||
|
||||
// mu guards modifying the token.
|
||||
mu sync.Mutex
|
||||
|
||||
// Transport is the HTTP transport to use when making requests.
|
||||
// It will default to http.DefaultTransport if nil.
|
||||
// (It should never be an oauth.Transport.)
|
||||
Transport http.RoundTripper
|
||||
}
|
||||
|
||||
// Client returns an *http.Client that makes OAuth-authenticated requests.
|
||||
func (t *Transport) Client() *http.Client {
|
||||
return &http.Client{Transport: t}
|
||||
}
|
||||
|
||||
func (t *Transport) transport() http.RoundTripper {
|
||||
if t.Transport != nil {
|
||||
return t.Transport
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
// AuthCodeURL returns a URL that the end-user should be redirected to,
|
||||
// so that they may obtain an authorization code.
|
||||
func (c *Config) AuthCodeURL(state string) string {
|
||||
url_, err := url.Parse(c.AuthURL)
|
||||
if err != nil {
|
||||
panic("AuthURL malformed: " + err.Error())
|
||||
}
|
||||
q := url.Values{
|
||||
"response_type": {"code"},
|
||||
"client_id": {c.ClientId},
|
||||
"state": condVal(state),
|
||||
"scope": condVal(c.Scope),
|
||||
"redirect_uri": condVal(c.RedirectURL),
|
||||
"access_type": condVal(c.AccessType),
|
||||
"approval_prompt": condVal(c.ApprovalPrompt),
|
||||
}.Encode()
|
||||
if url_.RawQuery == "" {
|
||||
url_.RawQuery = q
|
||||
} else {
|
||||
url_.RawQuery += "&" + q
|
||||
}
|
||||
return url_.String()
|
||||
}
|
||||
|
||||
func condVal(v string) []string {
|
||||
if v == "" {
|
||||
return nil
|
||||
}
|
||||
return []string{v}
|
||||
}
|
||||
|
||||
// Exchange takes a code and gets access Token from the remote server.
|
||||
func (t *Transport) Exchange(code string) (*Token, error) {
|
||||
if t.Config == nil {
|
||||
return nil, OAuthError{"Exchange", "no Config supplied"}
|
||||
}
|
||||
|
||||
// If the transport or the cache already has a token, it is
|
||||
// passed to `updateToken` to preserve existing refresh token.
|
||||
tok := t.Token
|
||||
if tok == nil && t.TokenCache != nil {
|
||||
tok, _ = t.TokenCache.Token()
|
||||
}
|
||||
if tok == nil {
|
||||
tok = new(Token)
|
||||
}
|
||||
err := t.updateToken(tok, url.Values{
|
||||
"grant_type": {"authorization_code"},
|
||||
"redirect_uri": {t.RedirectURL},
|
||||
"scope": {t.Scope},
|
||||
"code": {code},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.Token = tok
|
||||
if t.TokenCache != nil {
|
||||
return tok, t.TokenCache.PutToken(tok)
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
// RoundTrip executes a single HTTP transaction using the Transport's
|
||||
// Token as authorization headers.
|
||||
//
|
||||
// This method will attempt to renew the Token if it has expired and may return
|
||||
// an error related to that Token renewal before attempting the client request.
|
||||
// If the Token cannot be renewed a non-nil os.Error value will be returned.
|
||||
// If the Token is invalid callers should expect HTTP-level errors,
|
||||
// as indicated by the Response's StatusCode.
|
||||
func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
accessToken, err := t.getAccessToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// To set the Authorization header, we must make a copy of the Request
|
||||
// so that we don't modify the Request we were given.
|
||||
// This is required by the specification of http.RoundTripper.
|
||||
req = cloneRequest(req)
|
||||
req.Header.Set("Authorization", "Bearer "+accessToken)
|
||||
|
||||
// Make the HTTP request.
|
||||
return t.transport().RoundTrip(req)
|
||||
}
|
||||
|
||||
func (t *Transport) getAccessToken() (string, error) {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if t.Token == nil {
|
||||
if t.Config == nil {
|
||||
return "", OAuthError{"RoundTrip", "no Config supplied"}
|
||||
}
|
||||
if t.TokenCache == nil {
|
||||
return "", OAuthError{"RoundTrip", "no Token supplied"}
|
||||
}
|
||||
var err error
|
||||
t.Token, err = t.TokenCache.Token()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh the Token if it has expired.
|
||||
if t.Expired() {
|
||||
if err := t.Refresh(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
if t.AccessToken == "" {
|
||||
return "", errors.New("no access token obtained from refresh")
|
||||
}
|
||||
return t.AccessToken, nil
|
||||
}
|
||||
|
||||
// cloneRequest returns a clone of the provided *http.Request.
|
||||
// The clone is a shallow copy of the struct and its Header map.
|
||||
func cloneRequest(r *http.Request) *http.Request {
|
||||
// shallow copy of the struct
|
||||
r2 := new(http.Request)
|
||||
*r2 = *r
|
||||
// deep copy of the Header
|
||||
r2.Header = make(http.Header)
|
||||
for k, s := range r.Header {
|
||||
r2.Header[k] = s
|
||||
}
|
||||
return r2
|
||||
}
|
||||
|
||||
// Refresh renews the Transport's AccessToken using its RefreshToken.
|
||||
func (t *Transport) Refresh() error {
|
||||
if t.Token == nil {
|
||||
return OAuthError{"Refresh", "no existing Token"}
|
||||
}
|
||||
if t.RefreshToken == "" {
|
||||
return OAuthError{"Refresh", "Token expired; no Refresh Token"}
|
||||
}
|
||||
if t.Config == nil {
|
||||
return OAuthError{"Refresh", "no Config supplied"}
|
||||
}
|
||||
|
||||
err := t.updateToken(t.Token, url.Values{
|
||||
"grant_type": {"refresh_token"},
|
||||
"refresh_token": {t.RefreshToken},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t.TokenCache != nil {
|
||||
return t.TokenCache.PutToken(t.Token)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthenticateClient gets an access Token using the client_credentials grant
|
||||
// type.
|
||||
func (t *Transport) AuthenticateClient() error {
|
||||
if t.Config == nil {
|
||||
return OAuthError{"Exchange", "no Config supplied"}
|
||||
}
|
||||
if t.Token == nil {
|
||||
t.Token = &Token{}
|
||||
}
|
||||
return t.updateToken(t.Token, url.Values{"grant_type": {"client_credentials"}})
|
||||
}
|
||||
|
||||
// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
|
||||
// implements the OAuth2 spec correctly
|
||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
|
||||
// In summary:
|
||||
// - Reddit only accepts client secret in the Authorization header
|
||||
// - Dropbox accepts either it in URL param or Auth header, but not both.
|
||||
// - Google only accepts URL param (not spec compliant?), not Auth header
|
||||
func providerAuthHeaderWorks(tokenURL string) bool {
|
||||
if strings.HasPrefix(tokenURL, "https://accounts.google.com/") ||
|
||||
strings.HasPrefix(tokenURL, "https://github.com/") ||
|
||||
strings.HasPrefix(tokenURL, "https://api.instagram.com/") ||
|
||||
strings.HasPrefix(tokenURL, "https://www.douban.com/") {
|
||||
// Some sites fail to implement the OAuth2 spec fully.
|
||||
return false
|
||||
}
|
||||
|
||||
// Assume the provider implements the spec properly
|
||||
// otherwise. We can add more exceptions as they're
|
||||
// discovered. We will _not_ be adding configurable hooks
|
||||
// to this package to let users select server bugs.
|
||||
return true
|
||||
}
|
||||
|
||||
// updateToken mutates both tok and v.
|
||||
func (t *Transport) updateToken(tok *Token, v url.Values) error {
|
||||
v.Set("client_id", t.ClientId)
|
||||
v.Set("client_secret", t.ClientSecret)
|
||||
client := &http.Client{Transport: t.transport()}
|
||||
req, err := http.NewRequest("POST", t.TokenURL, strings.NewReader(v.Encode()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
req.SetBasicAuth(t.ClientId, t.ClientSecret)
|
||||
r, err := client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Body.Close()
|
||||
if r.StatusCode != 200 {
|
||||
return OAuthError{"updateToken", "Unexpected HTTP status " + r.Status}
|
||||
}
|
||||
var b struct {
|
||||
Access string `json:"access_token"`
|
||||
Refresh string `json:"refresh_token"`
|
||||
ExpiresIn int64 `json:"expires_in"` // seconds
|
||||
Id string `json:"id_token"`
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
|
||||
switch content {
|
||||
case "application/x-www-form-urlencoded", "text/plain":
|
||||
vals, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b.Access = vals.Get("access_token")
|
||||
b.Refresh = vals.Get("refresh_token")
|
||||
b.ExpiresIn, _ = strconv.ParseInt(vals.Get("expires_in"), 10, 64)
|
||||
b.Id = vals.Get("id_token")
|
||||
default:
|
||||
if err = json.Unmarshal(body, &b); err != nil {
|
||||
return fmt.Errorf("got bad response from server: %q", body)
|
||||
}
|
||||
}
|
||||
if b.Access == "" {
|
||||
return errors.New("received empty access token from authorization server")
|
||||
}
|
||||
tok.AccessToken = b.Access
|
||||
// Don't overwrite `RefreshToken` with an empty value
|
||||
if b.Refresh != "" {
|
||||
tok.RefreshToken = b.Refresh
|
||||
}
|
||||
if b.ExpiresIn == 0 {
|
||||
tok.Expiry = time.Time{}
|
||||
} else {
|
||||
tok.Expiry = time.Now().Add(time.Duration(b.ExpiresIn) * time.Second)
|
||||
}
|
||||
if b.Id != "" {
|
||||
if tok.Extra == nil {
|
||||
tok.Extra = make(map[string]string)
|
||||
}
|
||||
tok.Extra["id_token"] = b.Id
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,122 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
url string
|
||||
token string
|
||||
}
|
||||
|
||||
func New(url, token string) *Client {
|
||||
return &Client{url, token}
|
||||
}
|
||||
|
||||
// Publish makes an http request to the remote queue
|
||||
// to insert work at the tail.
|
||||
func (c *Client) Publish(work *queue.Work) error {
|
||||
return c.send("POST", "/queue", work, nil)
|
||||
}
|
||||
|
||||
// Remove makes an http request to the remote queue to
|
||||
// remove the specified work item.
|
||||
func (c *Client) Remove(work *queue.Work) error {
|
||||
return c.send("DELETE", "/queue", work, nil)
|
||||
}
|
||||
|
||||
// Pull makes an http request to the remote queue to
|
||||
// retrieve work. This initiates a long poll and will
|
||||
// block until complete.
|
||||
func (c *Client) Pull() *queue.Work {
|
||||
out := &queue.Work{}
|
||||
err := c.send("POST", "/queue/pull", nil, out)
|
||||
if err != nil {
|
||||
// TODO handle error
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Pull makes an http request to the remote queue to
|
||||
// retrieve work. This initiates a long poll and will
|
||||
// block until complete.
|
||||
func (c *Client) PullClose(cn queue.CloseNotifier) *queue.Work {
|
||||
out := &queue.Work{}
|
||||
err := c.send("POST", "/queue/pull", nil, out)
|
||||
if err != nil {
|
||||
// TODO handle error
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// Ack makes an http request to the remote queue
|
||||
// to acknowledge an item in the queue was processed.
|
||||
func (c *Client) Ack(work *queue.Work) error {
|
||||
return c.send("POST", "/queue/ack", nil, nil)
|
||||
}
|
||||
|
||||
// Items makes an http request to the remote queue
|
||||
// to fetch a list of all work.
|
||||
func (c *Client) Items() []*queue.Work {
|
||||
out := []*queue.Work{}
|
||||
err := c.send("GET", "/queue/items", nil, &out)
|
||||
if err != nil {
|
||||
// TODO handle error
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// send is a helper function that makes an authenticated
|
||||
// request to the remote http plugin.
|
||||
func (c *Client) send(method, path string, in interface{}, out interface{}) error {
|
||||
url_, err := url.Parse(c.url + path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf io.ReadWriter
|
||||
if in != nil {
|
||||
buf = new(bytes.Buffer)
|
||||
err := json.NewEncoder(buf).Encode(in)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(method, url_.String(), buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Add("Authorization", "Bearer "+c.token)
|
||||
req.Header.Add("Content-Type", "application/json")
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if out == nil {
|
||||
return nil
|
||||
}
|
||||
return json.NewDecoder(resp.Body).Decode(out)
|
||||
}
|
||||
|
||||
// In order to implement PullClose() we'll need to use a custom transport:
|
||||
//
|
||||
// tr := &http.Transport{}
|
||||
// client := &http.Client{Transport: tr}
|
||||
// c := make(chan error, 1)
|
||||
// go func() { c <- f(client.Do(req)) }()
|
||||
// select {
|
||||
// case <-ctx.Done():
|
||||
// tr.CancelRequest(req)
|
||||
// <-c // Wait for f to return.
|
||||
// return ctx.Err()
|
||||
// case err := <-c:
|
||||
// return err
|
||||
// }
|
|
@ -1,111 +0,0 @@
|
|||
package plugin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/gin-gonic/gin"
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
)
|
||||
|
||||
// Handle returns an http.Handler that enables a remote
|
||||
// client to interop with a Queue over http.
|
||||
func Handle(queue queue.Queue, token string) http.Handler {
|
||||
r := gin.New()
|
||||
|
||||
// middleware to validate the authorization token
|
||||
// and to inject the queue into the http context.
|
||||
bearer := "Bearer " + token
|
||||
r.Use(func(c *gin.Context) {
|
||||
if c.Request.Header.Get("Authorization") != bearer {
|
||||
c.AbortWithStatus(403)
|
||||
return
|
||||
}
|
||||
c.Set("queue", queue)
|
||||
c.Next()
|
||||
})
|
||||
|
||||
r.POST("/queue", publish)
|
||||
r.DELETE("/queue", remove)
|
||||
r.POST("/queue/pull", pull)
|
||||
r.POST("/queue/ack", ack)
|
||||
r.POST("/queue/items", items)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// publish handles an http request to the queue
|
||||
// to insert work at the tail.
|
||||
func publish(c *gin.Context) {
|
||||
q := fromContext(c)
|
||||
work := &queue.Work{}
|
||||
if !c.Bind(work) {
|
||||
c.AbortWithStatus(400)
|
||||
return
|
||||
}
|
||||
err := q.Publish(work)
|
||||
if err != nil {
|
||||
c.Fail(500, err)
|
||||
return
|
||||
}
|
||||
c.Writer.WriteHeader(200)
|
||||
}
|
||||
|
||||
// remove handles an http request to the queue
|
||||
// to remove a work item.
|
||||
func remove(c *gin.Context) {
|
||||
q := fromContext(c)
|
||||
work := &queue.Work{}
|
||||
if !c.Bind(work) {
|
||||
c.AbortWithStatus(400)
|
||||
return
|
||||
}
|
||||
err := q.Remove(work)
|
||||
if err != nil {
|
||||
c.Fail(500, err)
|
||||
return
|
||||
}
|
||||
c.Writer.WriteHeader(200)
|
||||
}
|
||||
|
||||
// pull handles an http request to the queue
|
||||
// to retrieve work.
|
||||
func pull(c *gin.Context) {
|
||||
q := fromContext(c)
|
||||
work := q.PullClose(c.Writer)
|
||||
if work == nil {
|
||||
c.AbortWithStatus(500)
|
||||
return
|
||||
}
|
||||
c.JSON(200, work)
|
||||
}
|
||||
|
||||
// ack handles an http request to the queue
|
||||
// to confirm an item was successfully pulled.
|
||||
func ack(c *gin.Context) {
|
||||
q := fromContext(c)
|
||||
work := &queue.Work{}
|
||||
if !c.Bind(work) {
|
||||
c.AbortWithStatus(400)
|
||||
return
|
||||
}
|
||||
err := q.Ack(work)
|
||||
if err != nil {
|
||||
c.Fail(500, err)
|
||||
return
|
||||
}
|
||||
c.Writer.WriteHeader(200)
|
||||
}
|
||||
|
||||
// items handles an http request to the queue to
|
||||
// return a list of all work items.
|
||||
func items(c *gin.Context) {
|
||||
q := fromContext(c)
|
||||
items := q.Items()
|
||||
c.JSON(200, items)
|
||||
}
|
||||
|
||||
// helper function to retrieve the Queue from
|
||||
// the context and cast appropriately.
|
||||
func fromContext(c *gin.Context) queue.Queue {
|
||||
return c.MustGet("queue").(queue.Queue)
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package queue
|
||||
|
||||
type Queue interface {
|
||||
// Publish inserts work at the tail of this queue, waiting for
|
||||
// space to become available if the queue is full.
|
||||
Publish(*Work) error
|
||||
|
||||
// Remove removes the specified work item from this queue,
|
||||
// if it is present.
|
||||
Remove(*Work) error
|
||||
|
||||
// Pull retrieves and removes the head of this queue, waiting
|
||||
// if necessary until work becomes available.
|
||||
Pull() *Work
|
||||
|
||||
// PullClose retrieves and removes the head of this queue,
|
||||
// waiting if necessary until work becomes available. The
|
||||
// CloseNotifier should be provided to clone the channel
|
||||
// if the subscribing client terminates its connection.
|
||||
PullClose(CloseNotifier) *Work
|
||||
|
||||
// Ack acknowledges an item in the queue was processed.
|
||||
Ack(*Work) error
|
||||
|
||||
// Items returns a slice containing all of the work in this
|
||||
// queue, in proper sequence.
|
||||
Items() []*Work
|
||||
}
|
||||
|
||||
type CloseNotifier interface {
|
||||
// CloseNotify returns a channel that receives a single value
|
||||
// when the client connection has gone away.
|
||||
CloseNotify() <-chan bool
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
package queue
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
common "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
// Work represents an item for work to be
|
||||
// processed by a worker.
|
||||
type Work struct {
|
||||
System *common.System `json:"system"`
|
||||
User *common.User `json:"user"`
|
||||
Repo *common.Repo `json:"repo"`
|
||||
Build *common.Build `json:"build"`
|
||||
BuildPrev *common.Build `json:"build_last"`
|
||||
Keys *common.Keypair `json:"keypair"`
|
||||
Netrc *common.Netrc `json:"netrc"`
|
||||
Config []byte `json:"config"`
|
||||
Secret []byte `json:"secret"`
|
||||
}
|
||||
|
||||
// represents a worker that has connected
|
||||
// to the system in order to perform work
|
||||
type Worker struct {
|
||||
Name string
|
||||
Addr string
|
||||
IsHealthy bool
|
||||
}
|
||||
|
||||
// Ping pings to worker to verify it is
|
||||
// available and in good health.
|
||||
func (w *Worker) Ping() (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Logs fetches the logs for a work item.
|
||||
func (w *Worker) Logs() (io.Reader, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Cancel cancels a work item.
|
||||
func (w *Worker) Cancel() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// type Monitor struct {
|
||||
// manager *Manager
|
||||
// }
|
||||
|
||||
// func NewMonitor(manager *Manager) *Monitor {
|
||||
// return &Monitor{manager}
|
||||
// }
|
||||
|
||||
// // start is a helper function that is used to monitor
|
||||
// // all registered workers and ensure they are in a
|
||||
// // healthy state.
|
||||
// func (m *Monitor) Start() {
|
||||
// ticker := time.NewTicker(1 * time.Hour)
|
||||
// go func() {
|
||||
// for {
|
||||
// select {
|
||||
// case <-ticker.C:
|
||||
// workers := m.manager.Workers()
|
||||
// for _, worker := range workers {
|
||||
// // ping the worker to make sure it is
|
||||
// // available and still accepting builds.
|
||||
// if _, err := worker.Ping(); err != nil {
|
||||
// m.manager.SetHealth(worker, false)
|
||||
// } else {
|
||||
// m.manager.SetHealth(worker, true)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -1,74 +0,0 @@
|
|||
package client
|
||||
|
||||
// import (
|
||||
// "net"
|
||||
// "net/http"
|
||||
// "net/rpc"
|
||||
|
||||
// common "github.com/drone/drone/pkg/types"
|
||||
// )
|
||||
|
||||
// // Client communicates with a Remote plugin using the
|
||||
// // net/rpc protocol.
|
||||
// type Client struct {
|
||||
// *rpc.Client
|
||||
// }
|
||||
|
||||
// // New returns a new, remote datastore backend that connects
|
||||
// // via tcp and exchanges data using Go's RPC mechanism.
|
||||
// func New(conf *config.Config) (*Client, error) {
|
||||
// // conn, err := net.Dial("tcp", conf.Server.Addr)
|
||||
// // if err != nil {
|
||||
// // return nil, err
|
||||
// // }
|
||||
// // client := &Client{
|
||||
// // rpc.NewClient(conn),
|
||||
// // }
|
||||
// // return client, nil
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Login(token, secret string) (*common.User, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// // Repo fetches the named repository from the remote system.
|
||||
// func (c *Client) Repo(u *common.User, owner, repo string) (*common.Repo, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Perm(u *common.User, owner, repo string) (*common.Perm, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Script(u *common.User, r *common.Repo, b *common.Build) ([]byte, error) {
|
||||
// return nil, nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Status(u *common.User, r *common.Repo, b *common.Build, link string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Activate(u *common.User, r *common.Repo, k *common.Keypair, link string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Deactivate(u *common.User, r *common.Repo, link string) error {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// func (c *Client) Hook(r *http.Request) (*common.Hook, error) {
|
||||
// hook := new(common.Hook)
|
||||
// header := make(http.Header)
|
||||
// copyHeader(r.Header, header)
|
||||
|
||||
// return hook, nil
|
||||
// }
|
||||
|
||||
// func copyHeader(dst, src http.Header) {
|
||||
// for k, vv := range src {
|
||||
// for _, v := range vv {
|
||||
// dst.Add(k, v)
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -1,22 +0,0 @@
|
|||
package runner
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/drone/drone/pkg/queue"
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
type Runner interface {
|
||||
Run(work *queue.Work) error
|
||||
Cancel(*types.Job) error
|
||||
Logs(*types.Job) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// Updater defines a set of functions that are required for
|
||||
// the runner to sent Drone updates during a build.
|
||||
type Updater interface {
|
||||
SetBuild(*types.User, *types.Repo, *types.Build) error
|
||||
SetJob(*types.Repo, *types.Build, *types.Job) error
|
||||
SetLogs(*types.Repo, *types.Build, *types.Job, io.ReadCloser) error
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
type Agentstore struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
func NewAgentstore(db *sql.DB) *Agentstore {
|
||||
return &Agentstore{db}
|
||||
}
|
||||
|
||||
// Agent returns an agent by ID.
|
||||
func (db *Agentstore) Agent(build *types.Build) (string, error) {
|
||||
agent, err := getAgent(db, rebind(stmtAgentSelectAgentCommit), build.ID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return agent.Addr, nil
|
||||
}
|
||||
|
||||
// SetAgent updates an agent in the datastore.
|
||||
func (db *Agentstore) SetAgent(build *types.Build, addr string) error {
|
||||
agent := Agent{Addr: addr, BuildID: build.ID}
|
||||
return createAgent(db, rebind(stmtAgentInsert), &agent)
|
||||
}
|
||||
|
||||
type Agent struct {
|
||||
ID int64
|
||||
Addr string
|
||||
BuildID int64 `sql:"unique:ux_agent_build"`
|
||||
}
|
|
@ -1,184 +0,0 @@
|
|||
package builtin
|
||||
|
||||
// DO NOT EDIT
|
||||
// code generated by go:generate
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var _ = json.Marshal
|
||||
|
||||
// generic database interface, matching both *sql.Db and *sql.Tx
|
||||
type agentDB interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func getAgent(db agentDB, query string, args ...interface{}) (*Agent, error) {
|
||||
row := db.QueryRow(query, args...)
|
||||
return scanAgent(row)
|
||||
}
|
||||
|
||||
func getAgents(db agentDB, query string, args ...interface{}) ([]*Agent, error) {
|
||||
rows, err := db.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanAgents(rows)
|
||||
}
|
||||
|
||||
func createAgent(db agentDB, query string, v *Agent) error {
|
||||
var v0 string
|
||||
var v1 int64
|
||||
v0 = v.Addr
|
||||
v1 = v.BuildID
|
||||
|
||||
res, err := db.Exec(query,
|
||||
&v0,
|
||||
&v1,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.ID, err = res.LastInsertId()
|
||||
return err
|
||||
}
|
||||
|
||||
func updateAgent(db agentDB, query string, v *Agent) error {
|
||||
var v0 int64
|
||||
var v1 string
|
||||
var v2 int64
|
||||
v0 = v.ID
|
||||
v1 = v.Addr
|
||||
v2 = v.BuildID
|
||||
|
||||
_, err := db.Exec(query,
|
||||
&v1,
|
||||
&v2,
|
||||
&v0,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func scanAgent(row *sql.Row) (*Agent, error) {
|
||||
var v0 int64
|
||||
var v1 string
|
||||
var v2 int64
|
||||
|
||||
err := row.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Agent{}
|
||||
v.ID = v0
|
||||
v.Addr = v1
|
||||
v.BuildID = v2
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func scanAgents(rows *sql.Rows) ([]*Agent, error) {
|
||||
var err error
|
||||
var vv []*Agent
|
||||
for rows.Next() {
|
||||
var v0 int64
|
||||
var v1 string
|
||||
var v2 int64
|
||||
err = rows.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
)
|
||||
if err != nil {
|
||||
return vv, err
|
||||
}
|
||||
|
||||
v := &Agent{}
|
||||
v.ID = v0
|
||||
v.Addr = v1
|
||||
v.BuildID = v2
|
||||
vv = append(vv, v)
|
||||
}
|
||||
return vv, rows.Err()
|
||||
}
|
||||
|
||||
const stmtAgentSelectList = `
|
||||
SELECT
|
||||
agent_id
|
||||
,agent_addr
|
||||
,agent_build_id
|
||||
FROM agents
|
||||
`
|
||||
|
||||
const stmtAgentSelectRange = `
|
||||
SELECT
|
||||
agent_id
|
||||
,agent_addr
|
||||
,agent_commit_id
|
||||
FROM agents
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
const stmtAgentSelect = `
|
||||
SELECT
|
||||
agent_id
|
||||
,agent_addr
|
||||
,agent_commit_id
|
||||
FROM agents
|
||||
WHERE agent_id = ?
|
||||
`
|
||||
|
||||
const stmtAgentSelectAgentCommit = `
|
||||
SELECT
|
||||
agent_id
|
||||
,agent_addr
|
||||
,agent_commit_id
|
||||
FROM agents
|
||||
WHERE agent_commit_id = ?
|
||||
`
|
||||
|
||||
const stmtAgentSelectCount = `
|
||||
SELECT count(1)
|
||||
FROM agents
|
||||
`
|
||||
|
||||
const stmtAgentInsert = `
|
||||
INSERT INTO agents (
|
||||
agent_addr
|
||||
,agent_commit_id
|
||||
) VALUES (?,?);
|
||||
`
|
||||
|
||||
const stmtAgentUpdate = `
|
||||
UPDATE agents SET
|
||||
agent_addr = ?
|
||||
,agent_commit_id = ?
|
||||
WHERE agent_id = ?
|
||||
`
|
||||
|
||||
const stmtAgentDelete = `
|
||||
DELETE FROM agents
|
||||
WHERE agent_id = ?
|
||||
`
|
||||
|
||||
const stmtAgentTable = `
|
||||
CREATE TABLE IF NOT EXISTS agents (
|
||||
agent_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,agent_addr VARCHAR
|
||||
,agent_commit_idINTEGER
|
||||
);
|
||||
`
|
||||
|
||||
const stmtAgentAgentCommitIndex = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_agent_commit ON agents (agent_commit_id);
|
||||
`
|
|
@ -1,85 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
type Blob struct {
|
||||
ID int64
|
||||
Path string `sql:"unique:ux_blob_path"`
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type Blobstore struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
// Del removes an object from the blobstore.
|
||||
func (db *Blobstore) DelBlob(path string) error {
|
||||
blob, _ := getBlob(db, rebind(stmtBlobSelectBlobPath), path)
|
||||
if blob == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := db.Exec(rebind(stmtBlobDelete), blob.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get retrieves an object from the blobstore.
|
||||
func (db *Blobstore) GetBlob(path string) ([]byte, error) {
|
||||
blob, err := getBlob(db, rebind(stmtBlobSelectBlobPath), path)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return blob.Data, nil
|
||||
}
|
||||
|
||||
// GetBlobReader retrieves an object from the blobstore.
|
||||
// It is the caller's responsibility to call Close on
|
||||
// the ReadCloser when finished reading.
|
||||
func (db *Blobstore) GetBlobReader(path string) (io.ReadCloser, error) {
|
||||
var blob, err = db.GetBlob(path)
|
||||
var buf = bytes.NewBuffer(blob)
|
||||
return ioutil.NopCloser(buf), err
|
||||
}
|
||||
|
||||
// SetBlob inserts an object into the blobstore.
|
||||
func (db *Blobstore) SetBlob(path string, data []byte) error {
|
||||
blob, _ := getBlob(db, rebind(stmtBlobSelectBlobPath), path)
|
||||
if blob == nil {
|
||||
blob = &Blob{}
|
||||
}
|
||||
blob.Path = path
|
||||
blob.Data = data
|
||||
if blob.ID == 0 {
|
||||
return createBlob(db, rebind(stmtBlobInsert), blob)
|
||||
}
|
||||
return updateBlob(db, rebind(stmtBlobUpdate), blob)
|
||||
}
|
||||
|
||||
// SetBlobReader inserts an object into the blobstore by
|
||||
// consuming data from r until EOF.
|
||||
func (db *Blobstore) SetBlobReader(path string, r io.Reader) error {
|
||||
var data, _ = ioutil.ReadAll(r)
|
||||
return db.SetBlob(path, data)
|
||||
}
|
||||
|
||||
func NewBlobstore(db *sql.DB) *Blobstore {
|
||||
return &Blobstore{db}
|
||||
}
|
||||
|
||||
// Blob table name in database.
|
||||
const blobTable = "blobs"
|
||||
|
||||
const blobQuery = `
|
||||
SELECT *
|
||||
FROM blobs
|
||||
WHERE blob_path = ?;
|
||||
`
|
||||
|
||||
const blobDeleteStmt = `
|
||||
DELETE FROM blobs
|
||||
WHERE blob_path = ?;
|
||||
`
|
|
@ -1,184 +0,0 @@
|
|||
package builtin
|
||||
|
||||
// DO NOT EDIT
|
||||
// code generated by go:generate
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
var _ = json.Marshal
|
||||
|
||||
// generic database interface, matching both *sql.Db and *sql.Tx
|
||||
type blobDB interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func getBlob(db blobDB, query string, args ...interface{}) (*Blob, error) {
|
||||
row := db.QueryRow(query, args...)
|
||||
return scanBlob(row)
|
||||
}
|
||||
|
||||
func getBlobs(db blobDB, query string, args ...interface{}) ([]*Blob, error) {
|
||||
rows, err := db.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanBlobs(rows)
|
||||
}
|
||||
|
||||
func createBlob(db blobDB, query string, v *Blob) error {
|
||||
var v0 string
|
||||
var v1 []byte
|
||||
v0 = v.Path
|
||||
v1 = v.Data
|
||||
|
||||
res, err := db.Exec(query,
|
||||
&v0,
|
||||
&v1,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.ID, err = res.LastInsertId()
|
||||
return err
|
||||
}
|
||||
|
||||
func updateBlob(db blobDB, query string, v *Blob) error {
|
||||
var v0 int64
|
||||
var v1 string
|
||||
var v2 []byte
|
||||
v0 = v.ID
|
||||
v1 = v.Path
|
||||
v2 = v.Data
|
||||
|
||||
_, err := db.Exec(query,
|
||||
&v1,
|
||||
&v2,
|
||||
&v0,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func scanBlob(row *sql.Row) (*Blob, error) {
|
||||
var v0 int64
|
||||
var v1 string
|
||||
var v2 []byte
|
||||
|
||||
err := row.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Blob{}
|
||||
v.ID = v0
|
||||
v.Path = v1
|
||||
v.Data = v2
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func scanBlobs(rows *sql.Rows) ([]*Blob, error) {
|
||||
var err error
|
||||
var vv []*Blob
|
||||
for rows.Next() {
|
||||
var v0 int64
|
||||
var v1 string
|
||||
var v2 []byte
|
||||
err = rows.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
)
|
||||
if err != nil {
|
||||
return vv, err
|
||||
}
|
||||
|
||||
v := &Blob{}
|
||||
v.ID = v0
|
||||
v.Path = v1
|
||||
v.Data = v2
|
||||
vv = append(vv, v)
|
||||
}
|
||||
return vv, rows.Err()
|
||||
}
|
||||
|
||||
const stmtBlobSelectList = `
|
||||
SELECT
|
||||
blob_id
|
||||
,blob_path
|
||||
,blob_data
|
||||
FROM blobs
|
||||
`
|
||||
|
||||
const stmtBlobSelectRange = `
|
||||
SELECT
|
||||
blob_id
|
||||
,blob_path
|
||||
,blob_data
|
||||
FROM blobs
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
const stmtBlobSelect = `
|
||||
SELECT
|
||||
blob_id
|
||||
,blob_path
|
||||
,blob_data
|
||||
FROM blobs
|
||||
WHERE blob_id = ?
|
||||
`
|
||||
|
||||
const stmtBlobSelectBlobPath = `
|
||||
SELECT
|
||||
blob_id
|
||||
,blob_path
|
||||
,blob_data
|
||||
FROM blobs
|
||||
WHERE blob_path = ?
|
||||
`
|
||||
|
||||
const stmtBlobSelectCount = `
|
||||
SELECT count(1)
|
||||
FROM blobs
|
||||
`
|
||||
|
||||
const stmtBlobInsert = `
|
||||
INSERT INTO blobs (
|
||||
blob_path
|
||||
,blob_data
|
||||
) VALUES (?,?);
|
||||
`
|
||||
|
||||
const stmtBlobUpdate = `
|
||||
UPDATE blobs SET
|
||||
blob_path = ?
|
||||
,blob_data = ?
|
||||
WHERE blob_id = ?
|
||||
`
|
||||
|
||||
const stmtBlobDelete = `
|
||||
DELETE FROM blobs
|
||||
WHERE blob_id = ?
|
||||
`
|
||||
|
||||
const stmtBlobTable = `
|
||||
CREATE TABLE IF NOT EXISTS blobs (
|
||||
blob_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,blob_path VARCHAR
|
||||
,blob_data BLOB
|
||||
);
|
||||
`
|
||||
|
||||
const stmtBlobBlobPathIndex = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_blob_path ON blobs (blob_path);
|
||||
`
|
|
@ -1,66 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
)
|
||||
|
||||
func TestBlobstore(t *testing.T) {
|
||||
db := mustConnectTest()
|
||||
bs := NewBlobstore(db)
|
||||
defer db.Close()
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Blobstore", func() {
|
||||
|
||||
// before each test be sure to purge the package
|
||||
// table data from the database.
|
||||
g.BeforeEach(func() {
|
||||
db.Exec("DELETE FROM blobs")
|
||||
})
|
||||
|
||||
g.It("Should Set a Blob", func() {
|
||||
err := bs.SetBlob("foo", []byte("bar"))
|
||||
g.Assert(err == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should Set a Blob reader", func() {
|
||||
var buf bytes.Buffer
|
||||
buf.Write([]byte("bar"))
|
||||
err := bs.SetBlobReader("foo", &buf)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
})
|
||||
|
||||
g.It("Should Overwrite a Blob", func() {
|
||||
bs.SetBlob("foo", []byte("bar"))
|
||||
bs.SetBlob("foo", []byte("baz"))
|
||||
blob, err := bs.GetBlob("foo")
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(string(blob)).Equal("baz")
|
||||
})
|
||||
|
||||
g.It("Should Get a Blob", func() {
|
||||
bs.SetBlob("foo", []byte("bar"))
|
||||
blob, err := bs.GetBlob("foo")
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(string(blob)).Equal("bar")
|
||||
})
|
||||
|
||||
g.It("Should Get a Blob reader", func() {
|
||||
bs.SetBlob("foo", []byte("bar"))
|
||||
r, _ := bs.GetBlobReader("foo")
|
||||
blob, _ := ioutil.ReadAll(r)
|
||||
g.Assert(string(blob)).Equal("bar")
|
||||
})
|
||||
|
||||
g.It("Should Del a Blob", func() {
|
||||
bs.SetBlob("foo", []byte("bar"))
|
||||
err := bs.DelBlob("foo")
|
||||
g.Assert(err == nil).IsTrue()
|
||||
})
|
||||
|
||||
})
|
||||
}
|
|
@ -1,222 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
type Buildstore struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
func NewBuildstore(db *sql.DB) *Buildstore {
|
||||
return &Buildstore{db}
|
||||
}
|
||||
|
||||
// Build gets a build by ID
|
||||
func (db *Buildstore) Build(id int64) (*types.Build, error) {
|
||||
return getBuild(db, rebind(stmtBuildSelect), id)
|
||||
}
|
||||
|
||||
// BuildNumber gets the specified build number for the
|
||||
// named repository and build number
|
||||
func (db *Buildstore) BuildNumber(repo *types.Repo, seq int) (*types.Build, error) {
|
||||
return getBuild(db, rebind(stmtBuildSelectBuildNumber), repo.ID, seq)
|
||||
}
|
||||
|
||||
// BuildPullRequest gets the specific build for the
|
||||
// named repository and pull request number
|
||||
func (db *Buildstore) BuildPullRequestNumber(repo *types.Repo, seq int) (*types.Build, error) {
|
||||
return getBuild(db, rebind(stmtBuildSelectPullRequestNumber), repo.ID, seq)
|
||||
}
|
||||
|
||||
// BuildSha gets the specific build for the
|
||||
// named repository and sha
|
||||
func (db *Buildstore) BuildSha(repo *types.Repo, sha, branch string) (*types.Build, error) {
|
||||
return getBuild(db, rebind(stmtBuildSelectSha), repo.ID, sha, branch)
|
||||
}
|
||||
|
||||
// BuildLast gets the last executed build for the
|
||||
// named repository.
|
||||
func (db *Buildstore) BuildLast(repo *types.Repo, branch string) (*types.Build, error) {
|
||||
return getBuild(db, rebind(buildLastQuery), repo.ID, branch)
|
||||
}
|
||||
|
||||
// BuildList gets a list of recent builds for the
|
||||
// named repository.
|
||||
func (db *Buildstore) BuildList(repo *types.Repo, limit, offset int) ([]*types.Build, error) {
|
||||
return getBuilds(db, rebind(buildListQuery), repo.ID, limit, offset)
|
||||
}
|
||||
|
||||
// AddBuild inserts a new build in the datastore.
|
||||
func (db *Buildstore) AddBuild(build *types.Build) error {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
// extract the next build number from the database
|
||||
row := tx.QueryRow(rebind(buildNumberLast), build.RepoID)
|
||||
if row != nil {
|
||||
row.Scan(&build.Number)
|
||||
}
|
||||
|
||||
build.Number = build.Number + 1 // increment
|
||||
err = createBuild(tx, rebind(stmtBuildInsert), build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, job := range build.Jobs {
|
||||
job.BuildID = build.ID
|
||||
err := createJob(tx, rebind(stmtJobInsert), job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// SetBuild updates an existing build and build jobs.
|
||||
func (db *Buildstore) SetBuild(build *types.Build) error {
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
err = updateBuild(tx, rebind(stmtBuildUpdate), build)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, job := range build.Jobs {
|
||||
err = updateJob(tx, rebind(stmtJobUpdate), job)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// KillBuilds updates all pending or started builds
|
||||
// in the datastore settings the status to killed.
|
||||
func (db *Buildstore) KillBuilds() error {
|
||||
var _, err1 = db.Exec(rebind(buildKillStmt))
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
var _, err2 = db.Exec(rebind(jobKillStmt))
|
||||
return err2
|
||||
}
|
||||
|
||||
const stmtBuildSelectPullRequestNumber = stmtBuildSelectList + `
|
||||
WHERE build_repo_id = ?
|
||||
AND build_pull_request_number = ?
|
||||
ORDER BY build_number DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
const stmtBuildSelectSha = stmtBuildSelectList + `
|
||||
WHERE build_repo_id = ?
|
||||
AND build_commit_sha = ?
|
||||
AND build_commit_branch = ?
|
||||
ORDER BY build_number DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
// SQL query to retrieve the latest builds across all branches.
|
||||
const buildListQuery = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
WHERE build_repo_id = ?
|
||||
ORDER BY build_number DESC
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
// SQL query to retrieve the most recent build.
|
||||
// TODO exclude pull requests
|
||||
const buildLastQuery = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
WHERE build_repo_id = ?
|
||||
AND build_commit_branch = ?
|
||||
ORDER BY build_number DESC
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
// SQL statement to cancel all running builds.
|
||||
const buildKillStmt = `
|
||||
UPDATE builds SET build_status = 'killed'
|
||||
WHERE build_status IN ('pending', 'running');
|
||||
`
|
||||
|
||||
// SQL statement to cancel all running build jobs.
|
||||
const jobKillStmt = `
|
||||
UPDATE jobs SET job_status = 'killed'
|
||||
WHERE job_status IN ('pending', 'running');
|
||||
`
|
||||
|
||||
// SQL statement to retrieve the latest sequential
|
||||
// build number for a build
|
||||
const buildNumberLast = `
|
||||
SELECT MAX(build_number)
|
||||
FROM builds
|
||||
WHERE build_repo_id = ?
|
||||
`
|
|
@ -1,747 +0,0 @@
|
|||
package builtin
|
||||
|
||||
// DO NOT EDIT
|
||||
// code generated by go:generate
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
. "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
var _ = json.Marshal
|
||||
|
||||
// generic database interface, matching both *sql.Db and *sql.Tx
|
||||
type buildDB interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func getBuild(db buildDB, query string, args ...interface{}) (*Build, error) {
|
||||
row := db.QueryRow(query, args...)
|
||||
return scanBuild(row)
|
||||
}
|
||||
|
||||
func getBuilds(db buildDB, query string, args ...interface{}) ([]*Build, error) {
|
||||
rows, err := db.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanBuilds(rows)
|
||||
}
|
||||
|
||||
func createBuild(db buildDB, query string, v *Build) error {
|
||||
var v0 int64
|
||||
var v1 int
|
||||
var v2 string
|
||||
var v3 int64
|
||||
var v4 int64
|
||||
var v5 string
|
||||
var v6 string
|
||||
var v7 string
|
||||
var v8 string
|
||||
var v9 string
|
||||
var v10 string
|
||||
var v11 string
|
||||
var v12 string
|
||||
var v13 string
|
||||
var v14 int
|
||||
var v15 string
|
||||
var v16 string
|
||||
var v17 string
|
||||
var v18 string
|
||||
var v19 string
|
||||
var v20 string
|
||||
var v21 string
|
||||
var v22 string
|
||||
var v23 string
|
||||
var v24 string
|
||||
var v25 string
|
||||
v0 = v.RepoID
|
||||
v1 = v.Number
|
||||
v2 = v.Status
|
||||
v3 = v.Started
|
||||
v4 = v.Finished
|
||||
if v.Commit != nil {
|
||||
v5 = v.Commit.Sha
|
||||
v6 = v.Commit.Ref
|
||||
v7 = v.Commit.Link
|
||||
v8 = v.Commit.Branch
|
||||
v9 = v.Commit.Message
|
||||
v10 = v.Commit.Timestamp
|
||||
v11 = v.Commit.Remote
|
||||
if v.Commit.Author != nil {
|
||||
v12 = v.Commit.Author.Login
|
||||
v13 = v.Commit.Author.Email
|
||||
}
|
||||
}
|
||||
if v.PullRequest != nil {
|
||||
v14 = v.PullRequest.Number
|
||||
v15 = v.PullRequest.Title
|
||||
v16 = v.PullRequest.Link
|
||||
if v.PullRequest.Base != nil {
|
||||
v17 = v.PullRequest.Base.Sha
|
||||
v18 = v.PullRequest.Base.Ref
|
||||
v19 = v.PullRequest.Base.Link
|
||||
v20 = v.PullRequest.Base.Branch
|
||||
v21 = v.PullRequest.Base.Message
|
||||
v22 = v.PullRequest.Base.Timestamp
|
||||
v23 = v.PullRequest.Base.Remote
|
||||
if v.PullRequest.Base.Author != nil {
|
||||
v24 = v.PullRequest.Base.Author.Login
|
||||
v25 = v.PullRequest.Base.Author.Email
|
||||
}
|
||||
}
|
||||
}
|
||||
res, err := db.Exec(query,
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
&v8,
|
||||
&v9,
|
||||
&v10,
|
||||
&v11,
|
||||
&v12,
|
||||
&v13,
|
||||
&v14,
|
||||
&v15,
|
||||
&v16,
|
||||
&v17,
|
||||
&v18,
|
||||
&v19,
|
||||
&v20,
|
||||
&v21,
|
||||
&v22,
|
||||
&v23,
|
||||
&v24,
|
||||
&v25,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.ID, err = res.LastInsertId()
|
||||
return err
|
||||
}
|
||||
|
||||
func updateBuild(db buildDB, query string, v *Build) error {
|
||||
var v0 int64
|
||||
var v1 int64
|
||||
var v2 int
|
||||
var v3 string
|
||||
var v4 int64
|
||||
var v5 int64
|
||||
var v6 string
|
||||
var v7 string
|
||||
var v8 string
|
||||
var v9 string
|
||||
var v10 string
|
||||
var v11 string
|
||||
var v12 string
|
||||
var v13 string
|
||||
var v14 string
|
||||
var v15 int
|
||||
var v16 string
|
||||
var v17 string
|
||||
var v18 string
|
||||
var v19 string
|
||||
var v20 string
|
||||
var v21 string
|
||||
var v22 string
|
||||
var v23 string
|
||||
var v24 string
|
||||
var v25 string
|
||||
var v26 string
|
||||
v0 = v.ID
|
||||
v1 = v.RepoID
|
||||
v2 = v.Number
|
||||
v3 = v.Status
|
||||
v4 = v.Started
|
||||
v5 = v.Finished
|
||||
if v.Commit != nil {
|
||||
v6 = v.Commit.Sha
|
||||
v7 = v.Commit.Ref
|
||||
v8 = v.Commit.Link
|
||||
v9 = v.Commit.Branch
|
||||
v10 = v.Commit.Message
|
||||
v11 = v.Commit.Timestamp
|
||||
v12 = v.Commit.Remote
|
||||
if v.Commit.Author != nil {
|
||||
v13 = v.Commit.Author.Login
|
||||
v14 = v.Commit.Author.Email
|
||||
}
|
||||
}
|
||||
if v.PullRequest != nil {
|
||||
v15 = v.PullRequest.Number
|
||||
v16 = v.PullRequest.Title
|
||||
v17 = v.PullRequest.Link
|
||||
if v.PullRequest.Base != nil {
|
||||
v18 = v.PullRequest.Base.Sha
|
||||
v19 = v.PullRequest.Base.Ref
|
||||
v20 = v.PullRequest.Base.Link
|
||||
v21 = v.PullRequest.Base.Branch
|
||||
v22 = v.PullRequest.Base.Message
|
||||
v23 = v.PullRequest.Base.Timestamp
|
||||
v24 = v.PullRequest.Base.Remote
|
||||
if v.PullRequest.Base.Author != nil {
|
||||
v25 = v.PullRequest.Base.Author.Login
|
||||
v26 = v.PullRequest.Base.Author.Email
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err := db.Exec(query,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
&v8,
|
||||
&v9,
|
||||
&v10,
|
||||
&v11,
|
||||
&v12,
|
||||
&v13,
|
||||
&v14,
|
||||
&v15,
|
||||
&v16,
|
||||
&v17,
|
||||
&v18,
|
||||
&v19,
|
||||
&v20,
|
||||
&v21,
|
||||
&v22,
|
||||
&v23,
|
||||
&v24,
|
||||
&v25,
|
||||
&v26,
|
||||
&v0,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func scanBuild(row *sql.Row) (*Build, error) {
|
||||
var v0 int64
|
||||
var v1 int64
|
||||
var v2 int
|
||||
var v3 string
|
||||
var v4 int64
|
||||
var v5 int64
|
||||
var v6 string
|
||||
var v7 string
|
||||
var v8 string
|
||||
var v9 string
|
||||
var v10 string
|
||||
var v11 string
|
||||
var v12 string
|
||||
var v13 string
|
||||
var v14 string
|
||||
var v15 int
|
||||
var v16 string
|
||||
var v17 string
|
||||
var v18 string
|
||||
var v19 string
|
||||
var v20 string
|
||||
var v21 string
|
||||
var v22 string
|
||||
var v23 string
|
||||
var v24 string
|
||||
var v25 string
|
||||
var v26 string
|
||||
|
||||
err := row.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
&v8,
|
||||
&v9,
|
||||
&v10,
|
||||
&v11,
|
||||
&v12,
|
||||
&v13,
|
||||
&v14,
|
||||
&v15,
|
||||
&v16,
|
||||
&v17,
|
||||
&v18,
|
||||
&v19,
|
||||
&v20,
|
||||
&v21,
|
||||
&v22,
|
||||
&v23,
|
||||
&v24,
|
||||
&v25,
|
||||
&v26,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Build{}
|
||||
v.ID = v0
|
||||
v.RepoID = v1
|
||||
v.Number = v2
|
||||
v.Status = v3
|
||||
v.Started = v4
|
||||
v.Finished = v5
|
||||
v.Commit = &Commit{}
|
||||
v.Commit.Sha = v6
|
||||
v.Commit.Ref = v7
|
||||
v.Commit.Link = v8
|
||||
v.Commit.Branch = v9
|
||||
v.Commit.Message = v10
|
||||
v.Commit.Timestamp = v11
|
||||
v.Commit.Remote = v12
|
||||
v.Commit.Author = &Author{}
|
||||
v.Commit.Author.Login = v13
|
||||
v.Commit.Author.Email = v14
|
||||
v.PullRequest = &PullRequest{}
|
||||
v.PullRequest.Number = v15
|
||||
v.PullRequest.Title = v16
|
||||
v.PullRequest.Link = v17
|
||||
v.PullRequest.Base = &Commit{}
|
||||
v.PullRequest.Base.Sha = v18
|
||||
v.PullRequest.Base.Ref = v19
|
||||
v.PullRequest.Base.Link = v20
|
||||
v.PullRequest.Base.Branch = v21
|
||||
v.PullRequest.Base.Message = v22
|
||||
v.PullRequest.Base.Timestamp = v23
|
||||
v.PullRequest.Base.Remote = v24
|
||||
v.PullRequest.Base.Author = &Author{}
|
||||
v.PullRequest.Base.Author.Login = v25
|
||||
v.PullRequest.Base.Author.Email = v26
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func scanBuilds(rows *sql.Rows) ([]*Build, error) {
|
||||
var err error
|
||||
var vv []*Build
|
||||
for rows.Next() {
|
||||
var v0 int64
|
||||
var v1 int64
|
||||
var v2 int
|
||||
var v3 string
|
||||
var v4 int64
|
||||
var v5 int64
|
||||
var v6 string
|
||||
var v7 string
|
||||
var v8 string
|
||||
var v9 string
|
||||
var v10 string
|
||||
var v11 string
|
||||
var v12 string
|
||||
var v13 string
|
||||
var v14 string
|
||||
var v15 int
|
||||
var v16 string
|
||||
var v17 string
|
||||
var v18 string
|
||||
var v19 string
|
||||
var v20 string
|
||||
var v21 string
|
||||
var v22 string
|
||||
var v23 string
|
||||
var v24 string
|
||||
var v25 string
|
||||
var v26 string
|
||||
err = rows.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
&v8,
|
||||
&v9,
|
||||
&v10,
|
||||
&v11,
|
||||
&v12,
|
||||
&v13,
|
||||
&v14,
|
||||
&v15,
|
||||
&v16,
|
||||
&v17,
|
||||
&v18,
|
||||
&v19,
|
||||
&v20,
|
||||
&v21,
|
||||
&v22,
|
||||
&v23,
|
||||
&v24,
|
||||
&v25,
|
||||
&v26,
|
||||
)
|
||||
if err != nil {
|
||||
return vv, err
|
||||
}
|
||||
|
||||
v := &Build{}
|
||||
v.ID = v0
|
||||
v.RepoID = v1
|
||||
v.Number = v2
|
||||
v.Status = v3
|
||||
v.Started = v4
|
||||
v.Finished = v5
|
||||
v.Commit = &Commit{}
|
||||
v.Commit.Sha = v6
|
||||
v.Commit.Ref = v7
|
||||
v.Commit.Link = v8
|
||||
v.Commit.Branch = v9
|
||||
v.Commit.Message = v10
|
||||
v.Commit.Timestamp = v11
|
||||
v.Commit.Remote = v12
|
||||
v.Commit.Author = &Author{}
|
||||
v.Commit.Author.Login = v13
|
||||
v.Commit.Author.Email = v14
|
||||
v.PullRequest = &PullRequest{}
|
||||
v.PullRequest.Number = v15
|
||||
v.PullRequest.Title = v16
|
||||
v.PullRequest.Link = v17
|
||||
v.PullRequest.Base = &Commit{}
|
||||
v.PullRequest.Base.Sha = v18
|
||||
v.PullRequest.Base.Ref = v19
|
||||
v.PullRequest.Base.Link = v20
|
||||
v.PullRequest.Base.Branch = v21
|
||||
v.PullRequest.Base.Message = v22
|
||||
v.PullRequest.Base.Timestamp = v23
|
||||
v.PullRequest.Base.Remote = v24
|
||||
v.PullRequest.Base.Author = &Author{}
|
||||
v.PullRequest.Base.Author.Login = v25
|
||||
v.PullRequest.Base.Author.Email = v26
|
||||
vv = append(vv, v)
|
||||
}
|
||||
return vv, rows.Err()
|
||||
}
|
||||
|
||||
const stmtBuildSelectList = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
`
|
||||
|
||||
const stmtBuildSelectRange = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
const stmtBuildSelect = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
WHERE build_id = ?
|
||||
`
|
||||
|
||||
const stmtBuildSelectBuildRepoId = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
WHERE build_repo_id = ?
|
||||
`
|
||||
|
||||
const stmtBuildSelectBuildNumber = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
WHERE build_repo_id = ?
|
||||
AND build_number = ?
|
||||
`
|
||||
|
||||
const stmtBuildSelectCommitBranch = `
|
||||
SELECT
|
||||
build_id
|
||||
,build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
FROM builds
|
||||
WHERE build_branch = ?
|
||||
AND build_branch = ?
|
||||
`
|
||||
|
||||
const stmtBuildSelectCount = `
|
||||
SELECT count(1)
|
||||
FROM builds
|
||||
`
|
||||
|
||||
const stmtBuildInsert = `
|
||||
INSERT INTO builds (
|
||||
build_repo_id
|
||||
,build_number
|
||||
,build_status
|
||||
,build_started
|
||||
,build_finished
|
||||
,build_commit_sha
|
||||
,build_commit_ref
|
||||
,build_commit_link
|
||||
,build_commit_branch
|
||||
,build_commit_message
|
||||
,build_commit_timestamp
|
||||
,build_commit_remote
|
||||
,build_commit_author_login
|
||||
,build_commit_author_email
|
||||
,build_pull_request_number
|
||||
,build_pull_request_title
|
||||
,build_pull_request_link
|
||||
,build_pull_request_base_sha
|
||||
,build_pull_request_base_ref
|
||||
,build_pull_request_base_link
|
||||
,build_pull_request_base_branch
|
||||
,build_pull_request_base_message
|
||||
,build_pull_request_base_timestamp
|
||||
,build_pull_request_base_remote
|
||||
,build_pull_request_base_author_login
|
||||
,build_pull_request_base_author_email
|
||||
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);
|
||||
`
|
||||
|
||||
const stmtBuildUpdate = `
|
||||
UPDATE builds SET
|
||||
build_repo_id = ?
|
||||
,build_number = ?
|
||||
,build_status = ?
|
||||
,build_started = ?
|
||||
,build_finished = ?
|
||||
,build_commit_sha = ?
|
||||
,build_commit_ref = ?
|
||||
,build_commit_link = ?
|
||||
,build_commit_branch = ?
|
||||
,build_commit_message = ?
|
||||
,build_commit_timestamp = ?
|
||||
,build_commit_remote = ?
|
||||
,build_commit_author_login = ?
|
||||
,build_commit_author_email = ?
|
||||
,build_pull_request_number = ?
|
||||
,build_pull_request_title = ?
|
||||
,build_pull_request_link = ?
|
||||
,build_pull_request_base_sha = ?
|
||||
,build_pull_request_base_ref = ?
|
||||
,build_pull_request_base_link = ?
|
||||
,build_pull_request_base_branch = ?
|
||||
,build_pull_request_base_message = ?
|
||||
,build_pull_request_base_timestamp = ?
|
||||
,build_pull_request_base_remote = ?
|
||||
,build_pull_request_base_author_login = ?
|
||||
,build_pull_request_base_author_email = ?
|
||||
WHERE build_id = ?
|
||||
`
|
||||
|
||||
const stmtBuildDelete = `
|
||||
DELETE FROM builds
|
||||
WHERE build_id = ?
|
||||
`
|
||||
|
||||
const stmtBuildTable = `
|
||||
CREATE TABLE IF NOT EXISTS builds (
|
||||
build_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,build_repo_id INTEGER
|
||||
,build_number INTEGER
|
||||
,build_status VARCHAR
|
||||
,build_started INTEGER
|
||||
,build_finished INTEGER
|
||||
,build_commit_sha VARCHAR
|
||||
,build_commit_ref VARCHAR
|
||||
,build_commit_link VARCHAR
|
||||
,build_commit_branch VARCHAR
|
||||
,build_commit_message VARCHAR
|
||||
,build_commit_timestamp VARCHAR
|
||||
,build_commit_remote VARCHAR
|
||||
,build_commit_author_login VARCHAR
|
||||
,build_commit_author_email VARCHAR
|
||||
,build_pull_request_number INTEGER
|
||||
,build_pull_request_title VARCHAR
|
||||
,build_pull_request_link VARCHAR
|
||||
,build_pull_request_base_sha VARCHAR
|
||||
,build_pull_request_base_ref VARCHAR
|
||||
,build_pull_request_base_link VARCHAR
|
||||
,build_pull_request_base_branch VARCHAR
|
||||
,build_pull_request_base_message VARCHAR
|
||||
,build_pull_request_base_timestamp VARCHAR
|
||||
,build_pull_request_base_remote VARCHAR
|
||||
,build_pull_request_base_author_login VARCHAR
|
||||
,build_pull_request_base_author_email VARCHAR
|
||||
);
|
||||
`
|
||||
|
||||
const stmtBuildBuildRepoIdIndex = `
|
||||
CREATE INDEX IF NOT EXISTS ix_build_repo_id ON builds (build_repo_id);
|
||||
`
|
||||
|
||||
const stmtBuildBuildNumberIndex = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_build_number ON builds (build_repo_id,build_number);
|
||||
`
|
||||
|
||||
const stmtBuildCommitBranchIndex = `
|
||||
CREATE INDEX IF NOT EXISTS ix_commit_branch ON builds (build_branch,build_branch);
|
||||
`
|
|
@ -1,172 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
func TestCommitstore(t *testing.T) {
|
||||
db := mustConnectTest()
|
||||
bs := NewBuildstore(db)
|
||||
defer db.Close()
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Buildstore", func() {
|
||||
|
||||
// before each test be sure to purge the package
|
||||
// table data from the database.
|
||||
g.BeforeEach(func() {
|
||||
db.Exec("DELETE FROM builds")
|
||||
db.Exec("DELETE FROM jobs")
|
||||
})
|
||||
|
||||
g.It("Should Post a Build", func() {
|
||||
build := types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StateSuccess,
|
||||
Commit: &types.Commit{
|
||||
Ref: "refs/heads/master",
|
||||
Sha: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||
},
|
||||
}
|
||||
err := bs.AddBuild(&build)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(build.ID != 0).IsTrue()
|
||||
g.Assert(build.Number).Equal(1)
|
||||
g.Assert(build.Commit.Ref).Equal("refs/heads/master")
|
||||
g.Assert(build.Commit.Sha).Equal("85f8c029b902ed9400bc600bac301a0aadb144ac")
|
||||
})
|
||||
|
||||
g.It("Should Put a Build", func() {
|
||||
build := types.Build{
|
||||
RepoID: 1,
|
||||
Number: 5,
|
||||
Status: types.StatePending,
|
||||
Commit: &types.Commit{
|
||||
Ref: "refs/heads/master",
|
||||
Sha: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||
},
|
||||
}
|
||||
bs.AddBuild(&build)
|
||||
build.Status = types.StateRunning
|
||||
err1 := bs.SetBuild(&build)
|
||||
getbuild, err2 := bs.Build(build.ID)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
g.Assert(build.ID).Equal(getbuild.ID)
|
||||
g.Assert(build.RepoID).Equal(getbuild.RepoID)
|
||||
g.Assert(build.Status).Equal(getbuild.Status)
|
||||
g.Assert(build.Number).Equal(getbuild.Number)
|
||||
})
|
||||
|
||||
g.It("Should Get a Build", func() {
|
||||
build := types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StateSuccess,
|
||||
}
|
||||
bs.AddBuild(&build)
|
||||
getbuild, err := bs.Build(build.ID)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(build.ID).Equal(getbuild.ID)
|
||||
g.Assert(build.RepoID).Equal(getbuild.RepoID)
|
||||
g.Assert(build.Status).Equal(getbuild.Status)
|
||||
})
|
||||
|
||||
g.It("Should Get a Build by Number", func() {
|
||||
build1 := &types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StatePending,
|
||||
}
|
||||
build2 := &types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StatePending,
|
||||
}
|
||||
err1 := bs.AddBuild(build1)
|
||||
err2 := bs.AddBuild(build2)
|
||||
getbuild, err3 := bs.BuildNumber(&types.Repo{ID: 1}, build2.Number)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
g.Assert(err3 == nil).IsTrue()
|
||||
g.Assert(build2.ID).Equal(getbuild.ID)
|
||||
g.Assert(build2.RepoID).Equal(getbuild.RepoID)
|
||||
g.Assert(build2.Number).Equal(getbuild.Number)
|
||||
})
|
||||
|
||||
g.It("Should Kill Pending or Started Builds", func() {
|
||||
build1 := &types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StateRunning,
|
||||
}
|
||||
build2 := &types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StatePending,
|
||||
}
|
||||
bs.AddBuild(build1)
|
||||
bs.AddBuild(build2)
|
||||
err1 := bs.KillBuilds()
|
||||
getbuild1, err2 := bs.Build(build1.ID)
|
||||
getbuild2, err3 := bs.Build(build2.ID)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
g.Assert(err3 == nil).IsTrue()
|
||||
g.Assert(getbuild1.Status).Equal(types.StateKilled)
|
||||
g.Assert(getbuild2.Status).Equal(types.StateKilled)
|
||||
})
|
||||
|
||||
g.It("Should get recent Builds", func() {
|
||||
build1 := &types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StateFailure,
|
||||
}
|
||||
build2 := &types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StateSuccess,
|
||||
}
|
||||
bs.AddBuild(build1)
|
||||
bs.AddBuild(build2)
|
||||
builds, err := bs.BuildList(&types.Repo{ID: 1}, 20, 0)
|
||||
g.Assert(err == nil).IsTrue()
|
||||
g.Assert(len(builds)).Equal(2)
|
||||
g.Assert(builds[0].ID).Equal(build2.ID)
|
||||
g.Assert(builds[0].RepoID).Equal(build2.RepoID)
|
||||
g.Assert(builds[0].Status).Equal(build2.Status)
|
||||
})
|
||||
//
|
||||
// g.It("Should get the last Commit", func() {
|
||||
// commit1 := &types.Commit{
|
||||
// RepoID: 1,
|
||||
// State: types.StateFailure,
|
||||
// Branch: "master",
|
||||
// Ref: "refs/heads/master",
|
||||
// Sha: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||
// }
|
||||
// commit2 := &types.Commit{
|
||||
// RepoID: 1,
|
||||
// State: types.StateFailure,
|
||||
// Branch: "master",
|
||||
// Ref: "refs/heads/master",
|
||||
// Sha: "8d6a233744a5dcacbf2605d4592a4bfe8b37320d",
|
||||
// }
|
||||
// commit3 := &types.Commit{
|
||||
// RepoID: 1,
|
||||
// State: types.StateSuccess,
|
||||
// Branch: "dev",
|
||||
// Ref: "refs/heads/dev",
|
||||
// Sha: "85f8c029b902ed9400bc600bac301a0aadb144ac",
|
||||
// }
|
||||
// err1 := bs.AddCommit(commit1)
|
||||
// err2 := bs.AddCommit(commit2)
|
||||
// err3 := bs.AddCommit(commit3)
|
||||
// last, err4 := bs.CommitLast(&types.Repo{ID: 1}, "master")
|
||||
// g.Assert(err1 == nil).IsTrue()
|
||||
// g.Assert(err2 == nil).IsTrue()
|
||||
// g.Assert(err3 == nil).IsTrue()
|
||||
// g.Assert(err4 == nil).IsTrue()
|
||||
// g.Assert(last.ID).Equal(commit2.ID)
|
||||
// g.Assert(last.RepoID).Equal(commit2.RepoID)
|
||||
// g.Assert(last.Sequence).Equal(commit2.Sequence)
|
||||
// })
|
||||
})
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
type Jobstore struct {
|
||||
*sql.DB
|
||||
}
|
||||
|
||||
func NewJobstore(db *sql.DB) *Jobstore {
|
||||
return &Jobstore{db}
|
||||
}
|
||||
|
||||
// Job returns a Job by ID.
|
||||
func (db *Jobstore) Job(id int64) (*types.Job, error) {
|
||||
return getJob(db, rebind(stmtJobSelect), id)
|
||||
}
|
||||
|
||||
// JobNumber returns a job by sequence number.
|
||||
func (db *Jobstore) JobNumber(build *types.Build, seq int) (*types.Job, error) {
|
||||
return getJob(db, rebind(stmtJobSelectBuildNumber), build.ID, seq)
|
||||
}
|
||||
|
||||
// JobList returns a list of all build jobs
|
||||
func (db *Jobstore) JobList(build *types.Build) ([]*types.Job, error) {
|
||||
return getJobs(db, rebind(stmtJobSelectJobBuildId), build.ID)
|
||||
}
|
||||
|
||||
// SetJob updates an existing build job.
|
||||
func (db *Jobstore) SetJob(job *types.Job) error {
|
||||
return updateJob(db, rebind(stmtJobUpdate), job)
|
||||
}
|
||||
|
||||
// AddJob inserts a build job.
|
||||
func (db *Jobstore) AddJob(job *types.Job) error {
|
||||
return createJob(db, rebind(stmtJobInsert), job)
|
||||
}
|
|
@ -1,300 +0,0 @@
|
|||
package builtin
|
||||
|
||||
// DO NOT EDIT
|
||||
// code generated by go:generate
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
. "github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
var _ = json.Marshal
|
||||
|
||||
// generic database interface, matching both *sql.Db and *sql.Tx
|
||||
type jobDB interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
Query(query string, args ...interface{}) (*sql.Rows, error)
|
||||
QueryRow(query string, args ...interface{}) *sql.Row
|
||||
}
|
||||
|
||||
func getJob(db jobDB, query string, args ...interface{}) (*Job, error) {
|
||||
row := db.QueryRow(query, args...)
|
||||
return scanJob(row)
|
||||
}
|
||||
|
||||
func getJobs(db jobDB, query string, args ...interface{}) ([]*Job, error) {
|
||||
rows, err := db.Query(query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
return scanJobs(rows)
|
||||
}
|
||||
|
||||
func createJob(db jobDB, query string, v *Job) error {
|
||||
var v0 int64
|
||||
var v1 int
|
||||
var v2 string
|
||||
var v3 int
|
||||
var v4 int64
|
||||
var v5 int64
|
||||
var v6 []byte
|
||||
v0 = v.BuildID
|
||||
v1 = v.Number
|
||||
v2 = v.Status
|
||||
v3 = v.ExitCode
|
||||
v4 = v.Started
|
||||
v5 = v.Finished
|
||||
v6, _ = json.Marshal(v.Environment)
|
||||
|
||||
res, err := db.Exec(query,
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
v.ID, err = res.LastInsertId()
|
||||
return err
|
||||
}
|
||||
|
||||
func updateJob(db jobDB, query string, v *Job) error {
|
||||
var v0 int64
|
||||
var v1 int64
|
||||
var v2 int
|
||||
var v3 string
|
||||
var v4 int
|
||||
var v5 int64
|
||||
var v6 int64
|
||||
var v7 []byte
|
||||
v0 = v.ID
|
||||
v1 = v.BuildID
|
||||
v2 = v.Number
|
||||
v3 = v.Status
|
||||
v4 = v.ExitCode
|
||||
v5 = v.Started
|
||||
v6 = v.Finished
|
||||
v7, _ = json.Marshal(v.Environment)
|
||||
|
||||
_, err := db.Exec(query,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
&v0,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
func scanJob(row *sql.Row) (*Job, error) {
|
||||
var v0 int64
|
||||
var v1 int64
|
||||
var v2 int
|
||||
var v3 string
|
||||
var v4 int
|
||||
var v5 int64
|
||||
var v6 int64
|
||||
var v7 []byte
|
||||
|
||||
err := row.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := &Job{}
|
||||
v.ID = v0
|
||||
v.BuildID = v1
|
||||
v.Number = v2
|
||||
v.Status = v3
|
||||
v.ExitCode = v4
|
||||
v.Started = v5
|
||||
v.Finished = v6
|
||||
json.Unmarshal(v7, &v.Environment)
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func scanJobs(rows *sql.Rows) ([]*Job, error) {
|
||||
var err error
|
||||
var vv []*Job
|
||||
for rows.Next() {
|
||||
var v0 int64
|
||||
var v1 int64
|
||||
var v2 int
|
||||
var v3 string
|
||||
var v4 int
|
||||
var v5 int64
|
||||
var v6 int64
|
||||
var v7 []byte
|
||||
err = rows.Scan(
|
||||
&v0,
|
||||
&v1,
|
||||
&v2,
|
||||
&v3,
|
||||
&v4,
|
||||
&v5,
|
||||
&v6,
|
||||
&v7,
|
||||
)
|
||||
if err != nil {
|
||||
return vv, err
|
||||
}
|
||||
|
||||
v := &Job{}
|
||||
v.ID = v0
|
||||
v.BuildID = v1
|
||||
v.Number = v2
|
||||
v.Status = v3
|
||||
v.ExitCode = v4
|
||||
v.Started = v5
|
||||
v.Finished = v6
|
||||
json.Unmarshal(v7, &v.Environment)
|
||||
vv = append(vv, v)
|
||||
}
|
||||
return vv, rows.Err()
|
||||
}
|
||||
|
||||
const stmtJobSelectList = `
|
||||
SELECT
|
||||
job_id
|
||||
,job_build_id
|
||||
,job_number
|
||||
,job_status
|
||||
,job_exit_code
|
||||
,job_started
|
||||
,job_finished
|
||||
,job_environment
|
||||
FROM jobs
|
||||
`
|
||||
|
||||
const stmtJobSelectRange = `
|
||||
SELECT
|
||||
job_id
|
||||
,job_build_id
|
||||
,job_number
|
||||
,job_status
|
||||
,job_exit_code
|
||||
,job_started
|
||||
,job_finished
|
||||
,job_environment
|
||||
FROM jobs
|
||||
LIMIT ? OFFSET ?
|
||||
`
|
||||
|
||||
const stmtJobSelect = `
|
||||
SELECT
|
||||
job_id
|
||||
,job_build_id
|
||||
,job_number
|
||||
,job_status
|
||||
,job_exit_code
|
||||
,job_started
|
||||
,job_finished
|
||||
,job_environment
|
||||
FROM jobs
|
||||
WHERE job_id = ?
|
||||
`
|
||||
|
||||
const stmtJobSelectJobBuildId = `
|
||||
SELECT
|
||||
job_id
|
||||
,job_build_id
|
||||
,job_number
|
||||
,job_status
|
||||
,job_exit_code
|
||||
,job_started
|
||||
,job_finished
|
||||
,job_environment
|
||||
FROM jobs
|
||||
WHERE job_build_id = ?
|
||||
`
|
||||
|
||||
const stmtJobSelectBuildNumber = `
|
||||
SELECT
|
||||
job_id
|
||||
,job_build_id
|
||||
,job_number
|
||||
,job_status
|
||||
,job_exit_code
|
||||
,job_started
|
||||
,job_finished
|
||||
,job_environment
|
||||
FROM jobs
|
||||
WHERE job_build_id = ?
|
||||
AND job_number = ?
|
||||
`
|
||||
|
||||
const stmtJobSelectCount = `
|
||||
SELECT count(1)
|
||||
FROM jobs
|
||||
`
|
||||
|
||||
const stmtJobInsert = `
|
||||
INSERT INTO jobs (
|
||||
job_build_id
|
||||
,job_number
|
||||
,job_status
|
||||
,job_exit_code
|
||||
,job_started
|
||||
,job_finished
|
||||
,job_environment
|
||||
) VALUES (?,?,?,?,?,?,?);
|
||||
`
|
||||
|
||||
const stmtJobUpdate = `
|
||||
UPDATE jobs SET
|
||||
job_build_id = ?
|
||||
,job_number = ?
|
||||
,job_status = ?
|
||||
,job_exit_code = ?
|
||||
,job_started = ?
|
||||
,job_finished = ?
|
||||
,job_environment = ?
|
||||
WHERE job_id = ?
|
||||
`
|
||||
|
||||
const stmtJobDelete = `
|
||||
DELETE FROM jobs
|
||||
WHERE job_id = ?
|
||||
`
|
||||
|
||||
const stmtJobTable = `
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
job_id INTEGER PRIMARY KEY AUTOINCREMENT
|
||||
,job_build_id INTEGER
|
||||
,job_number INTEGER
|
||||
,job_status VARCHAR(512)
|
||||
,job_exit_code INTEGER
|
||||
,job_started INTEGER
|
||||
,job_finished INTEGER
|
||||
,job_environmentVARCHAR(2048)
|
||||
);
|
||||
`
|
||||
|
||||
const stmtJobJobBuildIdIndex = `
|
||||
CREATE INDEX IF NOT EXISTS ix_job_build_id ON jobs (job_build_id);
|
||||
`
|
||||
|
||||
const stmtJobBuildNumberIndex = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS ux_build_number ON jobs (job_build_id,job_number);
|
||||
`
|
|
@ -1,119 +0,0 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/drone/drone/Godeps/_workspace/src/github.com/franela/goblin"
|
||||
"github.com/drone/drone/pkg/types"
|
||||
)
|
||||
|
||||
func TestBuildstore(t *testing.T) {
|
||||
db := mustConnectTest()
|
||||
bs := NewJobstore(db)
|
||||
cs := NewBuildstore(db)
|
||||
defer db.Close()
|
||||
|
||||
g := goblin.Goblin(t)
|
||||
g.Describe("Jobstore", func() {
|
||||
|
||||
// before each test we purge the package table data from the database.
|
||||
g.BeforeEach(func() {
|
||||
db.Exec("DELETE FROM jobs")
|
||||
db.Exec("DELETE FROM builds")
|
||||
})
|
||||
|
||||
g.It("Should Set a job", func() {
|
||||
job := &types.Job{
|
||||
BuildID: 1,
|
||||
Status: "pending",
|
||||
ExitCode: 0,
|
||||
Number: 1,
|
||||
}
|
||||
err1 := bs.AddJob(job)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
g.Assert(job.ID != 0).IsTrue()
|
||||
|
||||
job.Status = "started"
|
||||
err2 := bs.SetJob(job)
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
|
||||
getjob, err3 := bs.Job(job.ID)
|
||||
g.Assert(err3 == nil).IsTrue()
|
||||
g.Assert(getjob.Status).Equal(job.Status)
|
||||
})
|
||||
|
||||
g.It("Should Get a Job by ID", func() {
|
||||
job := &types.Job{
|
||||
BuildID: 1,
|
||||
Status: "pending",
|
||||
ExitCode: 1,
|
||||
Number: 1,
|
||||
Environment: map[string]string{"foo": "bar"},
|
||||
}
|
||||
err1 := bs.AddJob(job)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
g.Assert(job.ID != 0).IsTrue()
|
||||
|
||||
getjob, err2 := bs.Job(job.ID)
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
g.Assert(getjob.ID).Equal(job.ID)
|
||||
g.Assert(getjob.Status).Equal(job.Status)
|
||||
g.Assert(getjob.ExitCode).Equal(job.ExitCode)
|
||||
g.Assert(getjob.Environment).Equal(job.Environment)
|
||||
g.Assert(getjob.Environment["foo"]).Equal("bar")
|
||||
})
|
||||
|
||||
g.It("Should Get a Job by Number", func() {
|
||||
job := &types.Job{
|
||||
BuildID: 1,
|
||||
Status: "pending",
|
||||
ExitCode: 1,
|
||||
Number: 1,
|
||||
}
|
||||
err1 := bs.AddJob(job)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
g.Assert(job.ID != 0).IsTrue()
|
||||
|
||||
getjob, err2 := bs.JobNumber(&types.Build{ID: 1}, 1)
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
g.Assert(getjob.ID).Equal(job.ID)
|
||||
g.Assert(getjob.Status).Equal(job.Status)
|
||||
})
|
||||
|
||||
g.It("Should Get a List of Jobs by Commit", func() {
|
||||
|
||||
build := types.Build{
|
||||
RepoID: 1,
|
||||
Status: types.StateSuccess,
|
||||
Jobs: []*types.Job{
|
||||
&types.Job{
|
||||
BuildID: 1,
|
||||
Status: "success",
|
||||
ExitCode: 0,
|
||||
Number: 1,
|
||||
},
|
||||
&types.Job{
|
||||
BuildID: 3,
|
||||
Status: "error",
|
||||
ExitCode: 1,
|
||||
Number: 2,
|
||||
},
|
||||
&types.Job{
|
||||
BuildID: 5,
|
||||
Status: "pending",
|
||||
ExitCode: 0,
|
||||
Number: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
//
|
||||
err1 := cs.AddBuild(&build)
|
||||
g.Assert(err1 == nil).IsTrue()
|
||||
getjobs, err2 := bs.JobList(&build)
|
||||
g.Assert(err2 == nil).IsTrue()
|
||||
g.Assert(len(getjobs)).Equal(3)
|
||||
g.Assert(getjobs[0].Number).Equal(1)
|
||||
g.Assert(getjobs[0].Status).Equal(types.StateSuccess)
|
||||
})
|
||||
})
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue