replace UI with single page application (#1704)

This commit is contained in:
Brad Rydzewski 2016-07-08 15:40:29 -07:00 committed by GitHub
parent 59b0200412
commit 4d1df2924c
94 changed files with 330 additions and 5912 deletions

View file

@ -1,23 +1,21 @@
workspace:
base: /drone
base: /go
path: src/github.com/drone/drone
pipeline:
test:
image: drone/golang:1.5
backend:
image: golang:1.6
environment:
- GO15VENDOREXPERIMENT=1
- GOPATH=/drone
commands:
- export PATH=$PATH:$GOPATH/bin
- make deps gen
- make test test_postgres test_mysql
build:
image: drone/golang:1.5
compile:
image: golang:1.6
environment:
- GO15VENDOREXPERIMENT=1
- GOPATH=/drone
- GOPATH=/go
commands:
- export PATH=$PATH:$GOPATH/bin
- make build

View file

@ -1 +1 @@
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9kcm9uZQogIHBhdGg6IHNyYy9naXRodWIuY29tL2Ryb25lL2Ryb25lCgpwaXBlbGluZToKICB0ZXN0OgogICAgaW1hZ2U6IGRyb25lL2dvbGFuZzoxLjUKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPMTVWRU5ET1JFWFBFUklNRU5UPTEKICAgICAgLSBHT1BBVEg9L2Ryb25lCiAgICBjb21tYW5kczoKICAgICAgLSBleHBvcnQgUEFUSD0kUEFUSDokR09QQVRIL2JpbgogICAgICAtIG1ha2UgZGVwcyBnZW4KICAgICAgLSBtYWtlIHRlc3QgdGVzdF9wb3N0Z3JlcyB0ZXN0X215c3FsCgogIGJ1aWxkOgogICAgaW1hZ2U6IGRyb25lL2dvbGFuZzoxLjUKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPMTVWRU5ET1JFWFBFUklNRU5UPTEKICAgICAgLSBHT1BBVEg9L2Ryb25lCiAgICBjb21tYW5kczoKICAgICAgLSBleHBvcnQgUEFUSD0kUEFUSDokR09QQVRIL2JpbgogICAgICAtIG1ha2UgYnVpbGQKICAgIHdoZW46CiAgICAgIGV2ZW50OiBwdXNoCgogIHB1Ymxpc2g6CiAgICBpbWFnZTogczMKICAgIGFjbDogcHVibGljLXJlYWQKICAgIGJ1Y2tldDogZG93bmxvYWRzLmRyb25lLmlvCiAgICBzb3VyY2U6IHJlbGVhc2UvKiovKi4qCiAgICB3aGVuOgogICAgICBldmVudDogcHVzaAogICAgICBicmFuY2g6IG1hc3RlcgoKICBkb2NrZXI6CiAgICByZXBvOiBkcm9uZS9kcm9uZQogICAgdGFnOiBbICIwLjUuMCIsICIwLjUiIF0KICAgIHN0b3JhZ2VfZHJpdmVyOiBvdmVybGF5CiAgICB3aGVuOgogICAgICBicmFuY2g6IG1hc3RlcgogICAgICBldmVudDogcHVzaAoKc2VydmljZXM6CiAgcG9zdGdyZXM6CiAgICBpbWFnZTogcG9zdGdyZXM6OS40LjUKICAgIGVudmlyb25tZW50OgogICAgICAtIFBPU1RHUkVTX1VTRVI9cG9zdGdyZXMKICBteXNxbDoKICAgIGltYWdlOiBteXNxbDo1LjYuMjcKICAgIGVudmlyb25tZW50OgogICAgICAtIE1ZU1FMX0RBVEFCQVNFPXRlc3QKICAgICAgLSBNWVNRTF9BTExPV19FTVBUWV9QQVNTV09SRD15ZXMK.PFyNsvPRNRtL_9Tlgsqa0IDMIgzUrlk53eV4QJjZ3hU
eyJhbGciOiJIUzI1NiJ9.d29ya3NwYWNlOgogIGJhc2U6IC9nbwogIHBhdGg6IHNyYy9naXRodWIuY29tL2Ryb25lL2Ryb25lCgpwaXBlbGluZToKICBiYWNrZW5kOgogICAgaW1hZ2U6IGdvbGFuZzoxLjYKICAgIGVudmlyb25tZW50OgogICAgICAtIEdPMTVWRU5ET1JFWFBFUklNRU5UPTEKICAgIGNvbW1hbmRzOgogICAgICAtIG1ha2UgZGVwcyBnZW4KICAgICAgLSBtYWtlIHRlc3QgdGVzdF9wb3N0Z3JlcyB0ZXN0X215c3FsCgogIGNvbXBpbGU6CiAgICBpbWFnZTogZ29sYW5nOjEuNgogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gR08xNVZFTkRPUkVYUEVSSU1FTlQ9MQogICAgICAtIEdPUEFUSD0vZ28KICAgIGNvbW1hbmRzOgogICAgICAtIGV4cG9ydCBQQVRIPSRQQVRIOiRHT1BBVEgvYmluCiAgICAgIC0gbWFrZSBidWlsZAogICAgd2hlbjoKICAgICAgZXZlbnQ6IHB1c2gKCiAgcHVibGlzaDoKICAgIGltYWdlOiBzMwogICAgYWNsOiBwdWJsaWMtcmVhZAogICAgYnVja2V0OiBkb3dubG9hZHMuZHJvbmUuaW8KICAgIHNvdXJjZTogcmVsZWFzZS8qKi8qLioKICAgIHdoZW46CiAgICAgIGV2ZW50OiBwdXNoCiAgICAgIGJyYW5jaDogbWFzdGVyCgogIGRvY2tlcjoKICAgIHJlcG86IGRyb25lL2Ryb25lCiAgICB0YWc6IFsgIjAuNS4wIiwgIjAuNSIgXQogICAgc3RvcmFnZV9kcml2ZXI6IG92ZXJsYXkKICAgIHdoZW46CiAgICAgIGJyYW5jaDogbWFzdGVyCiAgICAgIGV2ZW50OiBwdXNoCgpzZXJ2aWNlczoKICBwb3N0Z3JlczoKICAgIGltYWdlOiBwb3N0Z3Jlczo5LjQuNQogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gUE9TVEdSRVNfVVNFUj1wb3N0Z3JlcwogIG15c3FsOgogICAgaW1hZ2U6IG15c3FsOjUuNi4yNwogICAgZW52aXJvbm1lbnQ6CiAgICAgIC0gTVlTUUxfREFUQUJBU0U9dGVzdAogICAgICAtIE1ZU1FMX0FMTE9XX0VNUFRZX1BBU1NXT1JEPXllcwo.kQIwqIgs7PnoKIGmzJ6hlbWTbV5zK0w4HVWsux79P3s

3
.gitignore vendored
View file

@ -1,7 +1,6 @@
drone/drone
*.sqlite
*_gen.go
*.html
#*.css
*.txt
*.zip
@ -13,6 +12,8 @@ drone/drone
temp/
release/
server/frontend/bower_components
server/frontend/build
server/swagger/files/*.json
# vendored repositories that we don't actually need

View file

@ -10,22 +10,20 @@ endif
all: gen build_static
deps:
deps: deps_backend deps_frontend
deps_frontend:
go get -u github.com/drone/drone-ui/dist
deps_backend:
go get -u golang.org/x/tools/cmd/cover
go get -u github.com/eknkc/amber/...
go get -u github.com/eknkc/amber
go get -u github.com/jteeuwen/go-bindata/...
go get -u github.com/elazarl/go-bindata-assetfs/...
go get -u github.com/dchest/jsmin
go get -u github.com/franela/goblin
gen: gen_static gen_template gen_migrations
gen_static:
go generate github.com/drone/drone/static
gen: gen_template gen_migrations
gen_template:
go generate github.com/drone/drone/template
go generate github.com/drone/drone/server/template
gen_migrations:
go generate github.com/drone/drone/store/datastore/ddl

14
cache/helper.go vendored
View file

@ -53,6 +53,20 @@ func GetRepos(c context.Context, user *model.User) ([]*model.RepoLite, error) {
return repos, nil
}
// GetRepoMap returns the list of user repositories from the cache
// associated with the current context in a map structure.
func GetRepoMap(c context.Context, user *model.User) (map[string]bool, error) {
repos, err := GetRepos(c, user)
if err != nil {
return nil, err
}
repom := map[string]bool{}
for _, repo := range repos {
repom[repo.FullName] = true
}
return repom, nil
}
// DeleteRepos evicts the cached user repositories from the cache associated
// with the current context.
func DeleteRepos(c context.Context, user *model.User) error {

View file

@ -43,6 +43,9 @@ type Client interface {
// RepoPatch updates a repository.
RepoPatch(*model.Repo) (*model.Repo, error)
// RepoChown updates a repository owner.
RepoChown(string, string) (*model.Repo, error)
// RepoDel deletes a repository.
RepoDel(string, string) error

View file

@ -32,6 +32,7 @@ const (
pathFeed = "%s/api/user/feed"
pathRepos = "%s/api/user/repos"
pathRepo = "%s/api/repos/%s/%s"
pathChown = "%s/api/repos/%s/%s/chown"
pathEncrypt = "%s/api/repos/%s/%s/encrypt"
pathBuilds = "%s/api/repos/%s/%s/builds"
pathBuild = "%s/api/repos/%s/%s/builds/%v"
@ -153,6 +154,14 @@ func (c *client) RepoPost(owner string, name string) (*model.Repo, error) {
return out, err
}
// RepoChow updates a repository owner.
func (c *client) RepoChown(owner string, name string) (*model.Repo, error) {
out := new(model.Repo)
uri := fmt.Sprintf(pathChown, c.base, owner, name)
err := c.post(uri, nil, out)
return out, err
}
// RepoPatch updates a repository.
func (c *client) RepoPatch(in *model.Repo) (*model.Repo, error) {
out := new(model.Repo)

View file

@ -1,2 +0,0 @@
/etc/init/drone.conf
/etc/drone/dronerc

View file

@ -1,7 +0,0 @@
Package: drone
Version: 0.4
Section: base
Priority: optional
Architecture: amd64
Maintainer: Brad Rydzewski <brad@drone.io>
Description: Drone continuous integration server

View file

@ -1,24 +0,0 @@
#!/bin/sh
set -e
case "$1" in
abort-upgrade|abort-remove|abort-deconfigure|configure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
echo "Starting drone ..."
if [ -f /etc/init/drone.conf ]; then
if pidof /usr/local/bin/drone >/dev/null; then
service drone stop || exit $?
fi
service drone start && echo "Drone started."
fi
#DEBHELPER#
exit 0

View file

@ -1,26 +0,0 @@
#!/bin/sh
set -e
set -u
case "$1" in
remove|remove-in-favour|deconfigure|deconfigure-in-favour)
if [ -f /etc/init/drone.conf ]; then
echo "Stopping drone ..."
service drone stop || exit $?
echo "Drone Stopped."
fi
;;
upgrade|failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
#DEBHELPER#
exit 0

View file

@ -1,31 +0,0 @@
#!/bin/bash
# server configuration
SERVER_ADDR=":80"
#SERVER_CERT=""
#SERVER_KEY=""
# database configuration
DATABASE_DRIVER="sqlite3"
DATABASE_CONFIG="/var/lib/drone/drone.sqlite"
# remote configuration
CLIENT="" # oauth2 client. REQUIRED
SECRET="" # oauth2 secret. REQUIRED
REMOTE_DRIVER="github"
REMOTE_CONFIG="https://github.com?client_id=$CLIENT&client_secret=$SECRET"
# docker configuration
DOCKER_HOST="unix:///var/run/docker.sock"
#DOCKER_CERT=""
#DOCKER_KEY=""
#DOCKER_CA=""
# plugin configuration
PLUGIN_FILTER="plugins/*"

View file

@ -1,12 +0,0 @@
start on (filesystem and net-device-up)
chdir /var/lib/drone
console log
script
set -a
if [ -f /etc/drone/dronerc ]; then
. /etc/drone/dronerc
fi
/usr/local/bin/drone
end script

View file

@ -1,19 +0,0 @@
# /etc/nsswitch.conf
#
# Example configuration of GNU Name Service Switch functionality.
# If you have the `glibc-doc-reference' and `info' packages installed, try:
# `info libc "Name Service Switch"' for information about this file.
passwd: compat
group: compat
shadow: compat
hosts: files dns
networks: files
protocols: files
services: files
ethers: files
rpc: files
netgroup: nis

View file

@ -1,6 +0,0 @@
// +build ignore
// This program converts amber templates to standard
// Go template files.
package main

View file

@ -1,60 +0,0 @@
// +build ignore
// This program minifies JavaScript files
// $ go run generate-js.go -dir scripts/ -out scripts/drone.min.js
package main
import (
"bytes"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/dchest/jsmin"
)
var (
dir = flag.String("dir", "scripts/", "")
out = flag.String("o", "scripts/drone.min.js", "")
)
func main() {
flag.Parse()
var buf bytes.Buffer
// walk the directory tree and write all
// javascript files to the buffer.
filepath.Walk(*dir, func(path string, info os.FileInfo, err error) error {
if filepath.Ext(path) != ".js" {
return nil
}
f, err := os.Open(path)
if err != nil {
return nil
}
defer f.Close()
// write the file name to the minified output
fmt.Fprintf(&buf, "// %s\n", path)
// copy the file to the buffer
_, err = io.Copy(&buf, f)
return err
})
// minifies the javascript
data, err := jsmin.Minify(buf.Bytes())
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// write the minified output
ioutil.WriteFile(*out, data, 0700)
}

View file

@ -1,21 +0,0 @@
#!/bin/bash
set -e
cd /tmp
# cleanup previously downloaded and unpacked files.
rm -rf libsass
rm -rf sassc
# download the latest build of sassc
git clone --depth=1 git://github.com/sass/libsass.git
git clone --depth=1 git://github.com/sass/sassc.git
export SASS_LIBSASS_PATH=/tmp/libsass
# build the sassc binary
cd sassc
make
# isntall the sassc binary
install -t /usr/local/bin bin/sassc

View file

@ -1,18 +0,0 @@
#!/bin/bash
set -e
cd /tmp
# cleanup previously downloaded and unpacked files.
rm -rf sqlite-autoconf-3081101.tar.gz
rm -rf sqlite-autoconf-3081101
# download sqlite
curl -O https://www.sqlite.org/2015/sqlite-autoconf-3081101.tar.gz
tar xzf sqlite-autoconf-3081101.tar.gz
# build and install
cd sqlite-autoconf-3081101
./configure -prefix=/scratch/usr/local
make
make install

View file

@ -2,7 +2,6 @@ package router
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
@ -10,17 +9,19 @@ import (
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/router/middleware/token"
"github.com/drone/drone/server"
"github.com/drone/drone/static"
"github.com/drone/drone/template"
"github.com/drone/drone/server/template"
"github.com/drone/drone-ui/dist"
)
// Load loads the router
func Load(middleware ...gin.HandlerFunc) http.Handler {
e := gin.New()
e.Use(gin.Recovery())
e.SetHTMLTemplate(template.Load())
e.StaticFS("/static", static.FileSystem())
e.StaticFS("/static", dist.AssetFS())
e.Use(header.NoCache)
e.Use(header.Options)
@ -29,35 +30,11 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
e.Use(session.SetUser())
e.Use(token.Refresh)
e.GET("/", server.ShowIndex)
e.GET("/repos", server.ShowAllRepos)
e.GET("/login", server.ShowLogin)
e.GET("/login/form", server.ShowLoginForm)
e.GET("/logout", server.GetLogout)
e.NoRoute(server.ShowIndex)
// TODO below will Go away with React UI
settings := e.Group("/settings")
{
settings.Use(session.MustUser())
settings.GET("/profile", server.ShowUser)
}
repo := e.Group("/repos/:owner/:name")
{
repo.Use(session.SetRepo())
repo.Use(session.SetPerm())
repo.Use(session.MustPull)
repo.GET("", server.ShowRepo)
repo.GET("/builds/:number", server.ShowBuild)
repo.GET("/builds/:number/:job", server.ShowBuild)
repo_settings := repo.Group("/settings")
{
repo_settings.GET("", session.MustPush, server.ShowRepoConf)
repo_settings.GET("/encrypt", session.MustPush, server.ShowRepoEncrypt)
repo_settings.GET("/badges", server.ShowRepoBadges)
}
}
// TODO above will Go away with React UI
user := e.Group("/api/user")
@ -129,6 +106,16 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
stream.GET("/:owner/:name", server.GetRepoEvents)
stream.GET("/:owner/:name/:build/:number", server.GetStream)
}
ws := e.Group("/ws")
{
ws.GET("/feed", server.EventStream)
ws.GET("/logs/:owner/:name/:build/:number",
session.SetRepo(),
session.SetPerm(),
session.MustPull,
server.LogStream,
)
}
auth := e.Group("/authorize")
{
@ -184,34 +171,5 @@ func Load(middleware ...gin.HandlerFunc) http.Handler {
// bots.POST("/slack/:command", Slack)
// }
return normalize(e)
}
// THIS HACK JOB IS GOING AWAY SOON.
//
// normalize is a helper function to work around the following
// issue with gin. https://github.com/gin-gonic/gin/issues/388
func normalize(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
parts := strings.Split(r.URL.Path, "/")[1:]
switch parts[0] {
case "settings", "bots", "repos", "api", "login", "logout", "", "authorize", "hook", "static", "gitlab":
// no-op
default:
if len(parts) > 2 && parts[2] != "settings" {
parts = append(parts[:2], append([]string{"builds"}, parts[2:]...)...)
}
// prefix the URL with /repo so that it
// can be effectively routed.
parts = append([]string{"", "repos"}, parts...)
// reconstruct the path
r.URL.Path = strings.Join(parts, "/")
}
h.ServeHTTP(w, r)
})
return e
}

View file

@ -110,11 +110,7 @@ func GetLogin(c *gin.Context) {
}
httputil.SetCookie(c.Writer, c.Request, "user_sess", tokenstr)
redirect := httputil.GetCookie(c.Request, "user_last")
if len(redirect) == 0 {
redirect = "/"
}
c.Redirect(303, redirect)
c.Redirect(303, "/")
}

View file

@ -1,192 +1,15 @@
package server
import (
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/drone/drone/cache"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/shared/httputil"
"github.com/drone/drone/shared/token"
"github.com/drone/drone/store"
)
// ShowIndex serves the main Drone application page.
func ShowIndex(c *gin.Context) {
user := session.User(c)
if user == nil {
c.Redirect(http.StatusSeeOther, "/login")
return
}
// get the repository list from the cache
repos, err := cache.GetRepos(c, user)
if err != nil {
c.String(400, err.Error())
return
}
// filter to only show the currently active ones
activeRepos, err := store.GetRepoListOf(c, repos)
if err != nil {
c.String(400, err.Error())
return
}
c.HTML(200, "index.html", gin.H{
"User": user,
"Repos": activeRepos,
})
}
func ShowAllRepos(c *gin.Context) {
user := session.User(c)
if user == nil {
c.Redirect(http.StatusSeeOther, "/login")
return
}
// get the repository list from the cache
repos, err := cache.GetRepos(c, user)
if err != nil {
c.String(400, err.Error())
return
}
c.HTML(200, "repos.html", gin.H{
"User": user,
"Repos": repos,
})
}
func ShowLogin(c *gin.Context) {
c.HTML(200, "login.html", gin.H{"Error": c.Query("error")})
}
func ShowLoginForm(c *gin.Context) {
c.HTML(200, "login_form.html", gin.H{})
}
func ShowUser(c *gin.Context) {
user := session.User(c)
token, _ := token.New(
token.CsrfToken,
user.Login,
).Sign(user.Hash)
c.HTML(200, "user.html", gin.H{
"User": user,
"Csrf": token,
})
}
func ShowRepo(c *gin.Context) {
user := session.User(c)
repo := session.Repo(c)
builds, _ := store.GetBuildList(c, repo)
groups := []*model.BuildGroup{}
var curr *model.BuildGroup
for _, build := range builds {
date := time.Unix(build.Created, 0).Format("Jan 2 2006")
if curr == nil || curr.Date != date {
curr = &model.BuildGroup{}
curr.Date = date
groups = append(groups, curr)
}
curr.Builds = append(curr.Builds, build)
}
httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName)
c.HTML(200, "repo.html", gin.H{
"User": user,
"Repo": repo,
"Builds": builds,
"Groups": groups,
})
}
func ShowRepoConf(c *gin.Context) {
user := session.User(c)
repo := session.Repo(c)
token, _ := token.New(
token.CsrfToken,
user.Login,
).Sign(user.Hash)
c.HTML(200, "repo_config.html", gin.H{
"User": user,
"Repo": repo,
"Csrf": token,
"Link": httputil.GetURL(c.Request),
})
}
func ShowRepoEncrypt(c *gin.Context) {
user := session.User(c)
repo := session.Repo(c)
token, _ := token.New(
token.CsrfToken,
user.Login,
).Sign(user.Hash)
c.HTML(200, "repo_secret.html", gin.H{
"User": user,
"Repo": repo,
"Csrf": token,
})
}
func ShowRepoBadges(c *gin.Context) {
user := session.User(c)
repo := session.Repo(c)
c.HTML(200, "repo_badge.html", gin.H{
"User": user,
"Repo": repo,
"Link": httputil.GetURL(c.Request),
})
}
func ShowBuild(c *gin.Context) {
user := session.User(c)
repo := session.Repo(c)
num, _ := strconv.Atoi(c.Param("number"))
seq, _ := strconv.Atoi(c.Param("job"))
if seq == 0 {
seq = 1
}
build, err := store.GetBuildNumber(c, repo, num)
if err != nil {
c.AbortWithError(404, err)
return
}
jobs, err := store.GetJobList(c, build)
if err != nil {
c.AbortWithError(404, err)
return
}
var job *model.Job
for _, j := range jobs {
if j.Number == seq {
job = j
break
}
}
httputil.SetCookie(c.Writer, c.Request, "user_last", repo.FullName)
var csrf string
if user != nil {
@ -196,12 +19,20 @@ func ShowBuild(c *gin.Context) {
).Sign(user.Hash)
}
c.HTML(200, "build.html", gin.H{
"User": user,
"Repo": repo,
"Build": build,
"Jobs": jobs,
"Job": job,
"Csrf": csrf,
c.HTML(200, "index.html", gin.H{
"user": user,
"csrf": csrf,
})
}
// ShowLogin is a legacy endpoint that now redirects to
// initiliaze the oauth flow
func ShowLogin(c *gin.Context) {
c.Redirect(303, "/authorize")
}
// ShowLoginForm displays a login form for systems like Gogs that do not
// yet support oauth workflows.
func ShowLoginForm(c *gin.Context) {
c.HTML(200, "login.html", gin.H{})
}

View file

@ -5,17 +5,18 @@ import (
"encoding/json"
"io"
"strconv"
"github.com/gin-gonic/gin"
"time"
"github.com/drone/drone/bus"
"github.com/drone/drone/cache"
"github.com/drone/drone/model"
"github.com/drone/drone/router/middleware/session"
"github.com/drone/drone/store"
"github.com/drone/drone/stream"
log "github.com/Sirupsen/logrus"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"github.com/manucorporat/sse"
)
@ -30,14 +31,14 @@ func GetRepoEvents(c *gin.Context) {
defer func() {
bus.Unsubscribe(c, eventc)
close(eventc)
log.Infof("closed event stream")
logrus.Infof("closed event stream")
}()
c.Stream(func(w io.Writer) bool {
select {
case event := <-eventc:
if event == nil {
log.Infof("nil event received")
logrus.Infof("nil event received")
return false
}
@ -75,13 +76,13 @@ func GetStream(c *gin.Context) {
build, err := store.GetBuildNumber(c, repo, buildn)
if err != nil {
log.Debugln("stream cannot get build number.", err)
logrus.Debugln("stream cannot get build number.", err)
c.AbortWithError(404, err)
return
}
job, err := store.GetJobNumber(c, build, jobn)
if err != nil {
log.Debugln("stream cannot get job number.", err)
logrus.Debugln("stream cannot get job number.", err)
c.AbortWithError(404, err)
return
}
@ -112,5 +113,174 @@ func GetStream(c *gin.Context) {
c.Writer.Flush()
}
log.Debugf("Closed stream %s#%d", repo.FullName, build.Number)
logrus.Debugf("Closed stream %s#%d", repo.FullName, build.Number)
}
var (
// Time allowed to write the file to the client.
writeWait = 5 * time.Second
// Time allowed to read the next pong message from the client.
pongWait = 60 * time.Second
// Send pings to client with this period. Must be less than pongWait.
pingPeriod = 30 * time.Second
)
// LogStream streams the build log output to the client.
func LogStream(c *gin.Context) {
repo := session.Repo(c)
buildn, _ := strconv.Atoi(c.Param("build"))
jobn, _ := strconv.Atoi(c.Param("number"))
c.Writer.Header().Set("Content-Type", "text/event-stream")
build, err := store.GetBuildNumber(c, repo, buildn)
if err != nil {
logrus.Debugln("stream cannot get build number.", err)
c.AbortWithError(404, err)
return
}
job, err := store.GetJobNumber(c, build, jobn)
if err != nil {
logrus.Debugln("stream cannot get job number.", err)
c.AbortWithError(404, err)
return
}
if job.Status != model.StatusRunning {
logrus.Debugln("stream not found.")
c.AbortWithStatus(404)
return
}
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
if _, ok := err.(websocket.HandshakeError); !ok {
logrus.Errorf("Cannot upgrade websocket. %s", err)
}
return
}
logrus.Debugf("Successfull upgraded websocket")
ticker := time.NewTicker(pingPeriod)
defer ticker.Stop()
rc, err := stream.Reader(c, stream.ToKey(job.ID))
if err != nil {
c.AbortWithError(404, err)
return
}
quitc := make(chan bool)
defer func() {
quitc <- true
close(quitc)
rc.Close()
ws.Close()
logrus.Debug("Successfully closed websocket")
}()
go func() {
defer func() {
recover()
}()
for {
select {
case <-quitc:
return
case <-ticker.C:
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait))
if err != nil {
return
}
}
}
}()
var scanner = bufio.NewScanner(rc)
var b []byte
for scanner.Scan() {
b = scanner.Bytes()
if len(b) == 0 {
continue
}
ws.SetWriteDeadline(time.Now().Add(writeWait))
ws.WriteMessage(websocket.TextMessage, b)
}
}
// EventStream produces the User event stream, sending all repository, build
// and agent events to the client.
func EventStream(c *gin.Context) {
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
if _, ok := err.(websocket.HandshakeError); !ok {
logrus.Errorf("Cannot upgrade websocket. %s", err)
}
return
}
logrus.Debugf("Successfull upgraded websocket")
user := session.User(c)
repo := map[string]bool{}
if user != nil {
repo, _ = cache.GetRepoMap(c, user)
}
ticker := time.NewTicker(pingPeriod)
quitc := make(chan bool)
eventc := make(chan *bus.Event, 10)
bus.Subscribe(c, eventc)
defer func() {
ticker.Stop()
bus.Unsubscribe(c, eventc)
quitc <- true
close(quitc)
close(eventc)
ws.Close()
logrus.Debug("Successfully closed websocket")
}()
go func() {
defer func() {
recover()
}()
for {
select {
case <-quitc:
return
case event := <-eventc:
if event == nil {
return
}
if repo[event.Repo.FullName] || !event.Repo.IsPrivate {
ws.SetWriteDeadline(time.Now().Add(writeWait))
ws.WriteJSON(event)
}
case <-ticker.C:
err := ws.WriteControl(websocket.PingMessage, []byte{}, time.Now().Add(writeWait))
if err != nil {
return
}
}
}
}()
reader(ws)
}
func reader(ws *websocket.Conn) {
defer ws.Close()
ws.SetReadLimit(512)
ws.SetReadDeadline(time.Now().Add(pongWait))
ws.SetPongHandler(func(string) error {
ws.SetReadDeadline(time.Now().Add(pongWait))
return nil
})
for {
_, _, err := ws.ReadMessage()
if err != nil {
break
}
}
}

View file

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta content="ie=edge" http-equiv="x-ua-compatible"/>
{{ if .csrf }}<meta name="csrf-token" content="{{ .csrf }}" />{{ end }}
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="/static/app.css" rel="stylesheet"/>
<link href="/static/favicon.ico" rel="icon" type="image/x-icon"/>
</head>
<body>
<div id="app"></div>
<script>
window.STATE_FROM_SERVER={{ . | json }};
</script>
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
<script src="/static/app.js"></script>
</body>
</html>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1" name="viewport"/>
<meta content="ie=edge" http-equiv="x-ua-compatible"/>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="/static/favicon.ico" rel="icon" type="image/x-icon"/>
</head>
<body>
<form actin="/authorize" method="post">
<input type="text" placeholder="Username" name="username" />
<input type="password" placeholder="Password" name="password" />
<input type="submit" value="Login" />
</form>
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
</body>
</html>

View file

@ -0,0 +1 @@
LOGOUT

View file

@ -0,0 +1,31 @@
package template
//go:generate go-bindata -pkg template -o template_gen.go files/
import (
"encoding/json"
"html/template"
"path/filepath"
)
// Load loads the templates from the embedded file map. This function will not
// compile if go generate is not executed before.
func Load() *template.Template {
dir, _ := AssetDir("files")
tmpl := template.New("_").Funcs(template.FuncMap{"json": marshal})
for _, name := range dir {
path := filepath.Join("files", name)
src := MustAsset(path)
tmpl = template.Must(
tmpl.New(name).Parse(string(src)),
)
}
return tmpl
}
// marshal is a helper function to render data as JSON inside the template.
func marshal(v interface{}) template.JS {
a, _ := json.Marshal(v)
return template.JS(a)
}

View file

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="466px" height="423.891px" viewBox="0 0 466 423.891" enable-background="new 0 0 466 423.891" xml:space="preserve">
<rect x="-43.5" y="37.005" display="none" fill-rule="evenodd" clip-rule="evenodd" fill="#E7E7E7" width="792" height="612"/>
<g>
<path id="outline_7_" fill-rule="evenodd" clip-rule="evenodd" d="M242.133,168.481h47.146v48.194h23.837
c11.008,0,22.33-1.962,32.755-5.494c5.123-1.736,10.872-4.154,15.926-7.193c-6.656-8.689-10.053-19.661-11.054-30.476
c-1.358-14.71,1.609-33.855,11.564-45.368l4.956-5.732l5.905,4.747c14.867,11.946,27.372,28.638,29.577,47.665
c17.901-5.266,38.921-4.02,54.701,5.088l6.475,3.734l-3.408,6.652c-13.345,26.046-41.246,34.113-68.524,32.687
c-40.817,101.663-129.68,149.794-237.428,149.794c-55.666,0-106.738-20.81-135.821-70.197l-0.477-0.807l-4.238-8.621
C4.195,271.415,0.93,247.6,3.145,223.803l0.664-7.127h40.315v-48.194h47.143v-47.145h94.292V74.191h56.574V168.481z"/>
<g display="none">
<path display="inline" fill="#394D54" d="M61.093,319.89c6.023,0,11.763-0.157,17.219-0.464c0.476-0.026,0.932-0.063,1.402-0.092
c0.005-0.002,0.008-0.002,0.012-0.002c13.872-0.855,25.876-2.708,35.902-5.57c0.002-0.002,0.004-0.002,0.006-0.002
c1.823-0.521,3.588-1.07,5.282-1.656c1.894-0.657,2.896-2.725,2.241-4.618c-0.656-1.895-2.722-2.899-4.618-2.24
c-12.734,4.412-29.535,6.842-50.125,7.298c-0.002,0-0.004,0-0.005,0c-10.477,0.232-21.93-0.044-34.352-0.843c0,0,0,0-0.001,0
c-0.635-0.038-1.259-0.075-1.9-0.118c-1.995-0.128-3.731,1.374-3.869,3.375c-0.136,1.999,1.376,3.73,3.375,3.866
c2.537,0.173,5.03,0.321,7.49,0.453c0.392,0.021,0.77,0.034,1.158,0.054l0,0C47.566,319.697,54.504,319.89,61.093,319.89z"/>
</g>
<g id="Containers_8_">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M86.209,179.744h3.227v34.052h-3.227V179.744z M80.02,179.744
h3.354v34.052H80.02V179.744z M73.828,179.744h3.354v34.052h-3.354V179.744z M67.636,179.744h3.354v34.052h-3.354V179.744z
M61.446,179.744H64.8v34.052h-3.354V179.744z M55.384,179.744h3.224v34.052h-3.224V179.744z M51.981,176.338h40.858v40.86H51.981
V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,132.598h3.229v34.051h-3.229V132.598z M127.165,132.598
h3.354v34.051h-3.354V132.598z M120.973,132.598h3.354v34.051h-3.354V132.598z M114.781,132.598h3.354v34.051h-3.354V132.598z
M108.593,132.598h3.352v34.051h-3.352V132.598z M102.531,132.598h3.222v34.051h-3.222V132.598z M99.124,129.193h40.863v40.859
H99.124V129.193z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,179.744h3.229v34.052h-3.229V179.744z M127.165,179.744
h3.354v34.052h-3.354V179.744z M120.973,179.744h3.354v34.052h-3.354V179.744z M114.781,179.744h3.354v34.052h-3.354V179.744z
M108.593,179.744h3.352v34.052h-3.352V179.744z M102.531,179.744h3.222v34.052h-3.222V179.744z M99.124,176.338h40.863v40.86
H99.124V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,179.744h3.225v34.052h-3.225V179.744z M174.31,179.744
h3.355v34.052h-3.355V179.744z M168.12,179.744h3.354v34.052h-3.354V179.744z M161.928,179.744h3.354v34.052h-3.354V179.744z
M155.736,179.744h3.354v34.052h-3.354V179.744z M149.676,179.744h3.222v34.052h-3.222V179.744z M146.271,176.338h40.861v40.86
h-40.861V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,132.598h3.225v34.051h-3.225V132.598z M174.31,132.598
h3.355v34.051h-3.355V132.598z M168.12,132.598h3.354v34.051h-3.354V132.598z M161.928,132.598h3.354v34.051h-3.354V132.598z
M155.736,132.598h3.354v34.051h-3.354V132.598z M149.676,132.598h3.222v34.051h-3.222V132.598z M146.271,129.193h40.861v40.859
h-40.861V129.193z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,179.744h3.226v34.052h-3.226V179.744z M221.457,179.744
h3.354v34.052h-3.354V179.744z M215.265,179.744h3.354v34.052h-3.354V179.744z M209.073,179.744h3.354v34.052h-3.354V179.744z
M202.884,179.744h3.354v34.052h-3.354V179.744z M196.821,179.744h3.224v34.052h-3.224V179.744z M193.416,176.338h40.861v40.86
h-40.861V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,132.598h3.226v34.051h-3.226V132.598z M221.457,132.598
h3.354v34.051h-3.354V132.598z M215.265,132.598h3.354v34.051h-3.354V132.598z M209.073,132.598h3.354v34.051h-3.354V132.598z
M202.884,132.598h3.354v34.051h-3.354V132.598z M196.821,132.598h3.224v34.051h-3.224V132.598z M193.416,129.193h40.861v40.859
h-40.861V129.193z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,85.451h3.226v34.053h-3.226V85.451z M221.457,85.451
h3.354v34.053h-3.354V85.451z M215.265,85.451h3.354v34.053h-3.354V85.451z M209.073,85.451h3.354v34.053h-3.354V85.451z
M202.884,85.451h3.354v34.053h-3.354V85.451z M196.821,85.451h3.224v34.053h-3.224V85.451z M193.416,82.048h40.861v40.86h-40.861
V82.048z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M274.792,179.744h3.224v34.052h-3.224V179.744z M268.602,179.744
h3.352v34.052h-3.352V179.744z M262.408,179.744h3.354v34.052h-3.354V179.744z M256.218,179.744h3.354v34.052h-3.354V179.744z
M250.026,179.744h3.354v34.052h-3.354V179.744z M243.964,179.744h3.227v34.052h-3.227V179.744z M240.561,176.338h40.86v40.86
h-40.86V176.338z"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M137.428,283.445c6.225,0,11.271,5.049,11.271,11.272
c0,6.225-5.046,11.271-11.271,11.271c-6.226,0-11.272-5.046-11.272-11.271C126.156,288.494,131.202,283.445,137.428,283.445"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M137.428,286.644c1.031,0,2.015,0.194,2.923,0.546
c-0.984,0.569-1.65,1.635-1.65,2.854c0,1.82,1.476,3.293,3.296,3.293c1.247,0,2.329-0.693,2.89-1.715
c0.395,0.953,0.615,1.999,0.615,3.097c0,4.458-3.615,8.073-8.073,8.073c-4.458,0-8.074-3.615-8.074-8.073
C129.354,290.258,132.971,286.644,137.428,286.644"/>
<path fill="#FFFFFF" d="M167.394,364.677c-27.916-13.247-43.239-31.256-51.765-50.915c-10.37,2.961-22.835,4.852-37.317,5.664
c-5.457,0.307-11.196,0.464-17.219,0.464c-6.942,0-14.26-0.205-21.94-0.613c25.6,25.585,57.094,45.283,115.408,45.645
C158.866,364.921,163.14,364.837,167.394,364.677z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="466px" height="423.891px" viewBox="0 0 466 423.891" enable-background="new 0 0 466 423.891" xml:space="preserve">
<rect x="-43.5" y="37.005" display="none" fill-rule="evenodd" clip-rule="evenodd" fill="#E7E7E7" width="792" height="612"/>
<g>
<path fill="#6699cc" id="outline_7_" fill-rule="evenodd" clip-rule="evenodd" d="M242.133,168.481h47.146v48.194h23.837
c11.008,0,22.33-1.962,32.755-5.494c5.123-1.736,10.872-4.154,15.926-7.193c-6.656-8.689-10.053-19.661-11.054-30.476
c-1.358-14.71,1.609-33.855,11.564-45.368l4.956-5.732l5.905,4.747c14.867,11.946,27.372,28.638,29.577,47.665
c17.901-5.266,38.921-4.02,54.701,5.088l6.475,3.734l-3.408,6.652c-13.345,26.046-41.246,34.113-68.524,32.687
c-40.817,101.663-129.68,149.794-237.428,149.794c-55.666,0-106.738-20.81-135.821-70.197l-0.477-0.807l-4.238-8.621
C4.195,271.415,0.93,247.6,3.145,223.803l0.664-7.127h40.315v-48.194h47.143v-47.145h94.292V74.191h56.574V168.481z"/>
<g display="none">
<path display="inline" fill="#394D54" d="M61.093,319.89c6.023,0,11.763-0.157,17.219-0.464c0.476-0.026,0.932-0.063,1.402-0.092
c0.005-0.002,0.008-0.002,0.012-0.002c13.872-0.855,25.876-2.708,35.902-5.57c0.002-0.002,0.004-0.002,0.006-0.002
c1.823-0.521,3.588-1.07,5.282-1.656c1.894-0.657,2.896-2.725,2.241-4.618c-0.656-1.895-2.722-2.899-4.618-2.24
c-12.734,4.412-29.535,6.842-50.125,7.298c-0.002,0-0.004,0-0.005,0c-10.477,0.232-21.93-0.044-34.352-0.843c0,0,0,0-0.001,0
c-0.635-0.038-1.259-0.075-1.9-0.118c-1.995-0.128-3.731,1.374-3.869,3.375c-0.136,1.999,1.376,3.73,3.375,3.866
c2.537,0.173,5.03,0.321,7.49,0.453c0.392,0.021,0.77,0.034,1.158,0.054l0,0C47.566,319.697,54.504,319.89,61.093,319.89z"/>
</g>
<g id="Containers_8_">
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M86.209,179.744h3.227v34.052h-3.227V179.744z M80.02,179.744
h3.354v34.052H80.02V179.744z M73.828,179.744h3.354v34.052h-3.354V179.744z M67.636,179.744h3.354v34.052h-3.354V179.744z
M61.446,179.744H64.8v34.052h-3.354V179.744z M55.384,179.744h3.224v34.052h-3.224V179.744z M51.981,176.338h40.858v40.86H51.981
V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,132.598h3.229v34.051h-3.229V132.598z M127.165,132.598
h3.354v34.051h-3.354V132.598z M120.973,132.598h3.354v34.051h-3.354V132.598z M114.781,132.598h3.354v34.051h-3.354V132.598z
M108.593,132.598h3.352v34.051h-3.352V132.598z M102.531,132.598h3.222v34.051h-3.222V132.598z M99.124,129.193h40.863v40.859
H99.124V129.193z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M133.354,179.744h3.229v34.052h-3.229V179.744z M127.165,179.744
h3.354v34.052h-3.354V179.744z M120.973,179.744h3.354v34.052h-3.354V179.744z M114.781,179.744h3.354v34.052h-3.354V179.744z
M108.593,179.744h3.352v34.052h-3.352V179.744z M102.531,179.744h3.222v34.052h-3.222V179.744z M99.124,176.338h40.863v40.86
H99.124V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,179.744h3.225v34.052h-3.225V179.744z M174.31,179.744
h3.355v34.052h-3.355V179.744z M168.12,179.744h3.354v34.052h-3.354V179.744z M161.928,179.744h3.354v34.052h-3.354V179.744z
M155.736,179.744h3.354v34.052h-3.354V179.744z M149.676,179.744h3.222v34.052h-3.222V179.744z M146.271,176.338h40.861v40.86
h-40.861V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M180.501,132.598h3.225v34.051h-3.225V132.598z M174.31,132.598
h3.355v34.051h-3.355V132.598z M168.12,132.598h3.354v34.051h-3.354V132.598z M161.928,132.598h3.354v34.051h-3.354V132.598z
M155.736,132.598h3.354v34.051h-3.354V132.598z M149.676,132.598h3.222v34.051h-3.222V132.598z M146.271,129.193h40.861v40.859
h-40.861V129.193z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,179.744h3.226v34.052h-3.226V179.744z M221.457,179.744
h3.354v34.052h-3.354V179.744z M215.265,179.744h3.354v34.052h-3.354V179.744z M209.073,179.744h3.354v34.052h-3.354V179.744z
M202.884,179.744h3.354v34.052h-3.354V179.744z M196.821,179.744h3.224v34.052h-3.224V179.744z M193.416,176.338h40.861v40.86
h-40.861V176.338z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,132.598h3.226v34.051h-3.226V132.598z M221.457,132.598
h3.354v34.051h-3.354V132.598z M215.265,132.598h3.354v34.051h-3.354V132.598z M209.073,132.598h3.354v34.051h-3.354V132.598z
M202.884,132.598h3.354v34.051h-3.354V132.598z M196.821,132.598h3.224v34.051h-3.224V132.598z M193.416,129.193h40.861v40.859
h-40.861V129.193z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M227.647,85.451h3.226v34.053h-3.226V85.451z M221.457,85.451
h3.354v34.053h-3.354V85.451z M215.265,85.451h3.354v34.053h-3.354V85.451z M209.073,85.451h3.354v34.053h-3.354V85.451z
M202.884,85.451h3.354v34.053h-3.354V85.451z M196.821,85.451h3.224v34.053h-3.224V85.451z M193.416,82.048h40.861v40.86h-40.861
V82.048z"/>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M274.792,179.744h3.224v34.052h-3.224V179.744z M268.602,179.744
h3.352v34.052h-3.352V179.744z M262.408,179.744h3.354v34.052h-3.354V179.744z M256.218,179.744h3.354v34.052h-3.354V179.744z
M250.026,179.744h3.354v34.052h-3.354V179.744z M243.964,179.744h3.227v34.052h-3.227V179.744z M240.561,176.338h40.86v40.86
h-40.86V176.338z"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M137.428,283.445c6.225,0,11.271,5.049,11.271,11.272
c0,6.225-5.046,11.271-11.271,11.271c-6.226,0-11.272-5.046-11.272-11.271C126.156,288.494,131.202,283.445,137.428,283.445"/>
<path fill="#6699cc" fill-rule="evenodd" clip-rule="evenodd" d="M137.428,286.644c1.031,0,2.015,0.194,2.923,0.546
c-0.984,0.569-1.65,1.635-1.65,2.854c0,1.82,1.476,3.293,3.296,3.293c1.247,0,2.329-0.693,2.89-1.715
c0.395,0.953,0.615,1.999,0.615,3.097c0,4.458-3.615,8.073-8.073,8.073c-4.458,0-8.074-3.615-8.074-8.073
C129.354,290.258,132.971,286.644,137.428,286.644"/>
<path fill="#FFFFFF" d="M167.394,364.677c-27.916-13.247-43.239-31.256-51.765-50.915c-10.37,2.961-22.835,4.852-37.317,5.664
c-5.457,0.307-11.196,0.464-17.219,0.464c-6.942,0-14.26-0.205-21.94-0.613c25.6,25.585,57.094,45.283,115.408,45.645
C158.866,364.921,163.14,364.837,167.394,364.677z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 6.4 KiB

View file

@ -1,13 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" width="466px" height="423.891px" viewBox="0 0 466 423.891" enable-background="new 0 0 466 423.891" xml:space="preserve">
<rect x="-43.5" y="37.005" display="none" fill-rule="evenodd" clip-rule="evenodd" fill="#E7E7E7" width="792" height="612"/>
<g>
<path fill="#FFF" id="outline_7_" fill-rule="evenodd" clip-rule="evenodd" d="M242.133,168.481h47.146v48.194h23.837 c11.008,0,22.33-1.962,32.755-5.494c5.123-1.736,10.872-4.154,15.926-7.193c-6.656-8.689-10.053-19.661-11.054-30.476 c-1.358-14.71,1.609-33.855,11.564-45.368l4.956-5.732l5.905,4.747c14.867,11.946,27.372,28.638,29.577,47.665 c17.901-5.266,38.921-4.02,54.701,5.088l6.475,3.734l-3.408,6.652c-13.345,26.046-41.246,34.113-68.524,32.687 c-40.817,101.663-129.68,149.794-237.428,149.794c-55.666,0-106.738-20.81-135.821-70.197l-0.477-0.807l-4.238-8.621 C4.195,271.415,0.93,247.6,3.145,223.803l0.664-7.127h40.315v-48.194h47.143v-47.145h94.292V74.191h56.574V168.481z"/>
<g display="none">
<path display="inline" fill="#FFF" d="M61.093,319.89c6.023,0,11.763-0.157,17.219-0.464c0.476-0.026,0.932-0.063,1.402-0.092 c0.005-0.002,0.008-0.002,0.012-0.002c13.872-0.855,25.876-2.708,35.902-5.57c0.002-0.002,0.004-0.002,0.006-0.002 c1.823-0.521,3.588-1.07,5.282-1.656c1.894-0.657,2.896-2.725,2.241-4.618c-0.656-1.895-2.722-2.899-4.618-2.24 c-12.734,4.412-29.535,6.842-50.125,7.298c-0.002,0-0.004,0-0.005,0c-10.477,0.232-21.93-0.044-34.352-0.843c0,0,0,0-0.001,0 c-0.635-0.038-1.259-0.075-1.9-0.118c-1.995-0.128-3.731,1.374-3.869,3.375c-0.136,1.999,1.376,3.73,3.375,3.866 c2.537,0.173,5.03,0.321,7.49,0.453c0.392,0.021,0.77,0.034,1.158,0.054l0,0C47.566,319.697,54.504,319.89,61.093,319.89z"/>
</g>
<path fill="#FFF" d="M167.394,364.677c-27.916-13.247-43.239-31.256-51.765-50.915c-10.37,2.961-22.835,4.852-37.317,5.664 c-5.457,0.307-11.196,0.464-17.219,0.464c-6.942,0-14.26-0.205-21.94-0.613c25.6,25.585,57.094,45.283,115.408,45.645 C158.866,364.921,163.14,364.837,167.394,364.677z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

View file

@ -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="#2b303b"
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:#2b303b;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:#2b303b;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:#2b303b;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

View file

@ -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="#666666"
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:#ffffff;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:#ffffff;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:#ffffff;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

View file

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
<g>
<path d="M255.637396,127.683191 C255.637396,198.196551 198.47207,255.363378 127.954205,255.363378 C57.4348387,255.363378 0.27026393,198.196551 0.27026393,127.683191 C0.27026393,57.1653255 57.4355894,0 127.954205,0 C198.472821,0 255.637396,57.1653255 255.637396,127.683191 L255.637396,127.683191 Z" fill="#DD4814"></path>
<path d="M41.1334194,110.63254 C31.7139707,110.63254 24.0827683,118.264493 24.0827683,127.683191 C24.0827683,137.097384 31.7139707,144.728587 41.1334194,144.728587 C50.5476129,144.728587 58.1788152,137.097384 58.1788152,127.683191 C58.1788152,118.264493 50.5476129,110.63254 41.1334194,110.63254 L41.1334194,110.63254 Z M162.848282,188.111202 C154.694569,192.820551 151.898839,203.240727 156.608938,211.389935 C161.313032,219.543648 171.733208,222.338628 179.886921,217.629279 C188.039883,212.925185 190.835613,202.505009 186.126264,194.350545 C181.42217,186.202088 170.995988,183.407109 162.848282,188.111202 L162.848282,188.111202 Z M78.1618299,127.683191 C78.1618299,110.836739 86.5295015,95.9534545 99.3332551,86.9409032 L86.8703343,66.0667683 C71.9555191,76.0365044 60.8581818,91.271132 56.2464282,109.113806 C61.6276833,113.504845 65.0720469,120.189372 65.0720469,127.68244 C65.0720469,135.171003 61.6276833,141.855531 56.2464282,146.246569 C60.852176,164.094499 71.9495132,179.329877 86.8703343,189.299613 L99.3332551,168.420223 C86.5295015,159.412927 78.1618299,144.530393 78.1618299,127.683191 L78.1618299,127.683191 Z M127.954205,77.8855601 C153.967109,77.8855601 175.30895,97.8302874 177.549138,123.265877 L201.839859,122.907777 C200.644692,104.129689 192.441431,87.2719765 179.836622,74.875871 C173.354792,77.3247625 165.86773,76.9501466 159.396411,73.2197537 C152.91383,69.4788504 148.849361,63.1681877 147.738276,56.3177478 C141.438123,54.5790499 134.807648,53.6271202 127.952704,53.6271202 C116.168446,53.6271202 105.026815,56.3950733 95.1344047,61.2913548 L106.979472,82.5175836 C113.351695,79.5521877 120.460387,77.8855601 127.954205,77.8855601 L127.954205,77.8855601 Z M127.954205,177.475566 C120.460387,177.475566 113.351695,175.808188 106.980223,172.843543 L95.1351554,194.069021 C105.027566,198.971308 116.169196,201.740012 127.954205,201.740012 C134.80915,201.740012 141.439625,200.787331 147.739026,199.043378 C148.850111,192.192938 152.916082,185.888282 159.397161,182.140622 C165.872985,178.404223 173.355543,178.036364 179.837372,180.485255 C192.442182,168.08915 200.645443,151.231437 201.84061,132.453349 L177.543883,132.095249 C175.30895,157.537595 153.967859,177.475566 127.954205,177.475566 L127.954205,177.475566 Z M162.842276,67.2446686 C170.995988,71.9532669 181.416915,69.1642933 186.121009,61.0105806 C190.830358,52.856868 188.041384,42.4359413 179.886921,37.7258416 C171.733208,33.0217478 161.313032,35.8167273 156.602182,43.9704399 C151.898839,52.1196481 154.693818,62.5405748 162.842276,67.2446686 L162.842276,67.2446686 Z" fill="#FFFFFF"></path>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -1,200 +0,0 @@
function JobViewModel(repo, build, job, status) {
var self = this;
self.status = status;
self.stream = function() {
$("#output").html("");
$("#restart").hide();
$("#cancel").show();
var buf = new Drone.Buffer();
buf.start(document.getElementById("output"));
$( "#tail" ).show();
$( "#tail" ).click(function() {
buf.autoFollow = !buf.autoFollow;
if (buf.autoFollow) {
$( "#tail i" ).text("pause");
$( "#tail" ).show();
// scroll to the bottom of the page
window.scrollTo(0, document.body.scrollHeight);
} else {
$( "#tail i" ).text("expand_more");
$( "#tail" ).show();
}
})
Stream(repo, build, job, function(out){
buf.write(out);
});
};
if (status !== "running" && status !== "pending") {
Logs(repo, build, job);
$("#restart").show();
}
if (status === "running") {
self.stream();
}
$("#restart").click(function() {
$("#restart").hide();
$("#output").html("");
$(".status").attr("class", "status pending").text("pending");
$.ajax({
url: "/api/repos/"+repo+"/builds/"+build,
type: "POST",
success: function( data ) { },
error: function( data ) {
console.log(data);
}
});
})
$("#cancel").click(function() {
$("#cancel").hide();
$.ajax({
url: "/api/repos/"+repo+"/builds/"+build+"/"+job,
type: "DELETE",
success: function( data ) { },
error: function( data ) {
console.log(data);
}
});
})
Subscribe(repo, function(data){
if (!data.jobs) {
return;
}
if (data.number !== build) {
return;
}
var before = self.status;
self.status = data.jobs[job-1].status;
// update the status for each job in the view
for (var i=0;i<data.jobs.length;i++) {
var job_ = data.jobs[i];
var el = $("[data-job="+job_.number+"]");
el.find(".status")
.attr("class", "status "+job_.status).text(job_.status);
switch (job_.status) {
case "running":
el.find(".msg-running").find("span").attr("data-livestamp", job_.started_at);
el.find(".msg-pending").hide();
el.find(".msg-running").show();
el.find(".msg-finished").hide();
el.find(".msg-exited").hide();
break;
case "pending":
el.find(".msg-pending").show();
el.find(".msg-running").hide();
el.find(".msg-finished").hide();
el.find(".msg-exited").hide();
break;
default:
el.find(".msg-finished").find("span").attr("data-livestamp", job_.finished_at);
el.find(".msg-exited").find("span").text(job_.exit_code);
el.find(".msg-pending").hide();
el.find(".msg-running").hide();
el.find(".msg-finished").show();
el.find(".msg-exited").show();
break;
}
}
var after = self.status;
// if the status has changed we should start
// streaming the build contents.
if (before !== after && after === "running") {
self.stream();
}
// if the status is changes to complete, we can show
// the restart button and hide the tail button.
if (after !== "pending" && after !== "running") {
$("#restart").show();
$("#cancel").hide();
$("#tail").hide();
}
}.bind(this));
}
function Logs(repo, build, job) {
$.get( "/api/repos/"+repo+"/logs/"+build+"/"+job, function( data ) {
var lines = JSON.parse(data);
var groups = {}
for (var i=0; i<lines.length; i++) {
var line = lines[i];
if (!line.proc) {
continue
}
var group = groups[line.proc];
if (!group) {
// create an element to hold the group of output
var pre = $("<pre>").attr("data-title", line.proc);
$("#output").append(pre);
// create the buffer for the group of output
var buf = new Drone.Buffer();
buf.start(pre[0]);
// add items to the group
group = {
pre: pre,
buf: buf,
};
groups[line.proc]=group;
}
group.buf.write(line.out+"\n");
}
for (var i=0; i<groups.length; i++) {
groups[i].buf.stop();
}
});
}
function Stream(repo, build, job, _callback) {
var callback = _callback;
var events = new EventSource("/api/stream/" + repo + "/" + build + "/" + job, {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);
};
};

View file

@ -1,11 +0,0 @@
$(function () {
// fetch the CSRF token from the meta tag
var token = $("meta[name='_csrf']").attr("content");
// ensure every Ajax request has the CSRF token
// included in the request's header.
$(document).ajaxSend(function(e, xhr, options) {
xhr.setRequestHeader("X-CSRF-TOKEN", token);
});
});

View file

@ -1,90 +0,0 @@
/**
* Creates an observable build job.
*/
function Job(data) {
this.number = ko.observable(data.number);
this.commit = ko.observable(data.commit);
this.started_at = ko.observable(data.started_at);
this.finished_at = ko.observable(data.finished_at);
this.exit_code = ko.observable(data.exit_code);
this.status = ko.observable(data.status);
this.environment = ko.observable(data.environment);
}
/**
* Creates an observable build.
*/
function Build(data) {
this.number = ko.observable(data.number);
this.commit = ko.observable(data.commit);
this.branch = ko.observable(data.branch);
this.author = ko.observable(data.author);
this.message = ko.observable(data.message);
this.status = ko.observable(data.status);
this.event = ko.observable(data.event);
this.started_at = ko.observable(data.started_at);
this.finished_at = ko.observable(data.finished_at);
}
/**
* Creates an observable repository.
*/
function Repo(data) {
this.full_name = ko.observable(data.full_name);
this.owner = ko.observable(data.owner);
this.name = ko.observable(data.name);
this.private = ko.observable(data.private);
this.trusted = ko.observable(data.trusted);
this.timeout = ko.observable(data.timeout);
this.avatar_url = ko.observable(data.avatar_url);
this.clone_url = ko.observable(data.clone_url);
this.link_url = ko.observable(data.link_url);
this.starred = ko.observable(data.starred || false);
this.events = ko.observable(data.events);
this.hook = ko.observable(new Hook(data));
}
/**
* Compares two repository objects by name. Used to sort
* a list of repositories.
*/
function RepoCompare(a, b) {
return a.full_name().toLowerCase() > b.full_name().toLowerCase() ? 1 : -1;
}
/**
* Creates an observable object that stores a list of hook event
* types (push, pull request, etc) and true or false if enabled.
*/
function Hook(repo) {
var data = {
"pull_request" : repo.events.indexOf("pull_request") !== -1,
"push" : repo.events.indexOf("push") !== -1,
"tag" : repo.events.indexOf("tag") !== -1,
"deploy" : repo.events.indexOf("deploy") !== -1
};
this.pull_request = ko.observable(data.pull_request);
this.push = ko.observable(data.push);
this.tag = ko.observable(data.tag);
this.deploy = ko.observable(data.deploy);
}
/**
* Creates an observable user.
*/
function User(data) {
this.login = ko.observable(data.login);
this.email = ko.observable(data.email);
this.avatar_url = ko.observable(data.avatar_url);
this.active = ko.observable(data.active);
this.admin = ko.observable(data.admin);
}
/**
* Compares two user objects by login. Used to sort
* a list of users.
*/
function UserCompare(a, b) {
return a.login().toLowerCase() > b.login().toLowerCase() ? 1 : -1;
}

View file

@ -1,74 +0,0 @@
function NodeViewModel() {
var self = this;
// handle requests to create a new node.
$(".modal-node button").click(function(e) {
var node = {
address : $("#addr").val(),
key : $("#key").val(),
cert : $("#cert").val(),
ca : $("#ca").val()
};
$.ajax({
url: "/api/nodes",
type: "POST",
contentType: "application/json",
data: JSON.stringify(node),
success: function( data ) {
// clears the form value
$(".modal-node input").val("");
var el = $("<div>").attr("class", "col-sm-4").append(
$("<div>").attr("class", "card").attr("data-id", data.id).append(
$("<div>").attr("class", "card-header").append(
$("<i>").attr("class", "linux_amd64")
)
).append(
$("<div>").attr("class", "card-block").append(
$("<h3>").text(data.address)
).append(
$("<p>").attr("class", "card-text").text(data.architecture)
).append(
$("<div>").attr("class", "btn-group").append(
$("<button>").attr("class","btn btn-danger").text("Delete")
)
)
)
)
$( ".node-row" ).prepend(el);
},
error: function( data ) {
console.log(data);
}
});
});
$(".node-row").on('click', '.btn-group .btn-danger', function(){
// gets the unique identifier for the click row, which is
// the user login id.
var id = $( this ).context
.parentNode
.parentNode
.parentNode.dataset.id;
var r = confirm("Are you sure you want to delete node "+id+"?");
if (r === false) {
return;
}
// makes an API call to delete the user and then, if successful,
// removes from the list.
$.ajax({
url: "/api/nodes/"+id,
type: "DELETE",
success: function( data ) {
$("[data-id='"+id+"']").parent().remove();
}
});
});
}

View file

@ -1,155 +0,0 @@
function RepoViewModel(repo) {
var self = this;
Subscribe(repo, function(data){
var el = $("[data-build="+data.number+"]");
if (el && el.length !== 0) {
// find the status label and adjust the
// build status accordingly.
var status = el.find(".status");
status.attr("class", "status "+data.status);
status.text(data.status);
return
}
// construct the build entry if it doesn't already exist
// so that we can append to the DOM. The code may not be
// pretty, but it is simple enough and it works.
var authoredOrDeployed = "authored"
var branchOrDeploy = data.branch
if ( data.event == "deployment" ) {
authoredOrDeployed = "deployed"
branchOrDeploy = data.deploy_to
}
el = $("<a>").attr("class", "card").attr("href", "/"+repo+"/"+data.number).attr("data-build", data.number)
.append(
$("<div>").attr("class", "card-header").append(
$("<img>").attr("src", data.author_avatar)
)
)
.append(
$("<div>").attr("class", "card-block").append(
$("<div>").append(
$("<div>").attr("class", "status "+ data.status).text(data.status)
).append(
$("<h3>").text(data.message)
)
).append(
$("<p>").attr("class","card-text").append(
$("<em>").text(data.author)
).append(
$("<span>").text(authoredOrDeployed)
).append(
$("<em>").attr("data-livestamp", data.created_at)
).append(
$("<span>").text("to")
).append(
$("<em>").text(branchOrDeploy)
)
)
).css("display", "flex").hide().fadeIn(1000);
// TODO it is very possible that the group may not
// exist, in which case the we'll need to create the
// gropu as well.
// append to the latest group in the timeline.
$(".card").first().before(el);
});
}
function Subscribe(repo, _callback) {
var callback = _callback;
var events = new EventSource("/api/stream/" + repo, {withCredentials: true});
events.onmessage = function (event) {
if (callback !== undefined) {
callback(JSON.parse(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);
};
};
function RepoConfigViewModel(repo) {
var self = this;
var timeoutLabel = $(".timeout-label")
$("input[type='range']").change(function(e) {
var timeout = parseInt(e.target.value);
timeoutLabel.text(timeout + " minutes");
patchRepo(repo, { timeout: timeout })
})
$("#push").change(function(e) {
patchRepo(repo, {
allow_push: e.target.checked,
})
})
$("#pull").change(function(e) {
patchRepo(repo, {
allow_pr: e.target.checked,
})
})
$("#tag").change(function(e) {
patchRepo(repo, {
allow_tag: e.target.checked,
})
})
$("#deploy").change(function(e) {
patchRepo(repo, {
allow_deploy: e.target.checked,
})
})
$("#trusted").change(function(e) {
patchRepo(repo, {
trusted: e.target.checked,
})
})
$(".btn-danger").click(function(e) {
var r = confirm("Are you sure you want to delete this repository?");
if (r !== false) {
deleteRepo(repo);
}
})
}
function deleteRepo(repo) {
$.ajax({
url: "/api/repos/"+repo,
type: "DELETE",
contentType: "application/json",
success: function() {
window.location.href="/";
},
});
}
function patchRepo(repo, data) {
$.ajax({
url: "/api/repos/"+repo,
type: "PATCH",
contentType: "application/json",
data: JSON.stringify(data)
});
}

View file

@ -1,31 +0,0 @@
function RepoListViewModel(repos) {
var self = this;
var mapped = $.map(repos, function(repo) {
return new Repo(repo)
});
self.repos = ko.observableArray(mapped);
self.newRepo = ko.observable();
self.addRepo = function() {
$.ajax({
url: "/api/repos/"+self.newRepo(),
type: "POST",
contentType: "application/json",
success: function( data ) {
self.repos.push(new Repo(data));
self.repos.sort(RepoCompare);
self.newRepo("");
},
error: function( data ) {
console.log(data);
}
});
};
}

View file

@ -1,66 +0,0 @@
var repoExpr = /.+\/.+/;
var remoteRepos = new Bloodhound({
queryTokenizer: Bloodhound.tokenizers.whitespace,
datumTokenizer: Bloodhound.tokenizers.obj.whitespace("full_name"),
identify: function(obj) { return obj.full_name; },
prefetch: '/api/user/repos/remote'
});
function reposWithDefaults(q, sync) {
if (q === "") {
sync(remoteRepos.all())
} else {
remoteRepos.search(q, sync);
}
}
$('.typeahead').typeahead({
hint: true,
highlight: true,
minLength: 0
},
{
name: "repos",
display: "full_name",
source: reposWithDefaults,
templates: {
empty: function(obj) {
if (obj.query.match(repoExpr) !== null) {
return [
"<div>",
"<div class='not-indexed-message'>",
"<p>",
"No matches found for",
"<em>",
obj.query,
"</em>",
"</p>",
"<p>",
"This repository may not be indexed yet.",
"<a href='/"+obj.query+"'>",
"Click here",
"</a>",
"to visit this repository page directly.",
"</p>",
"</div>",
"</div>"
].join("\n");
}
return [
"<div>",
"<div class='no-matches-message'>",
"No matches found",
"</div>",
"</div>"
].join("\n");
},
suggestion: function(obj) {
return "<div><div><img src='"+obj.avatar_url+"' width='32px' height='32px' /></div><div>"+ obj.full_name +"</div></div>";
}
}
}).bind('typeahead:select', function(ev, suggestion) {
window.location.href="/"+suggestion.full_name;
});

View file

@ -1,397 +0,0 @@
var Filter, STYLES, defaults, entities, extend, toHexString, _i, _results,
__slice = [].slice;
// theme stuff:
// http://ciembor.github.io/4bit
// https://github.com/lysyi3m/osx-terminal-themes
STYLES = {
'ef0': 'color:#000',
'ef1': 'color:#b87a7a',
'ef2': 'color:#7ab87a',
'ef3': 'color:#b8b87a',
'ef4': 'color:#7a7ab8',
'ef5': 'color:#b87ab8',
'ef6': 'color:#7ab8b8',
'ef7': 'color:#d9d9d9',
'ef8': 'color:#262626',
'ef9': 'color:#dbbdbd',
'ef10': 'color:#bddbbd',
'ef11': 'color:#dbdbbd',
'ef12': 'color:#bdbddb',
'ef13': 'color:#dbbddb',
'ef14': 'color:#bddbdb',
'ef15': 'color:#FFF',
'eb0': 'background-color:#000',
'eb1': 'background-color:#b87a7a',
'eb2': 'background-color:#7ab87a',
'eb3': 'background-color:#b8b87a',
'eb4': 'background-color:#7a7ab8',
'eb5': 'background-color:#b87ab8',
'eb6': 'background-color:#7ab8b8',
'eb7': 'background-color:#d9d9d9',
'eb8': 'background-color:#262626',
'eb9': 'background-color:#dbbdbd',
'eb10': 'background-color:#bddbbd',
'eb11': 'background-color:#dbdbbd',
'eb12': 'background-color:#bdbddb',
'eb13': 'background-color:#dbbddb',
'eb14': 'background-color:#bddbdb',
'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: /^\r+/,
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;
})();

View file

@ -1,52 +0,0 @@
;// Live commit updates
if(typeof(Drone) === 'undefined') { Drone = {}; }
(function () {
Drone.Buffer = function() {
this.lineFormatter = new Filter({stream: true, newline: false});
}
Drone.Buffer.prototype = {
lineBuffer: "",
autoFollow: false,
stoppingRefresh: false,
start: function(el) {
if(typeof(el) === 'string') {
this.el = document.getElementById(el);
} else {
this.el = el;
}
this.el.innerHTML="";
this.update();
},
stop: function() {
this.stoppingRefresh = true;
},
update: function() {
if(this.lineBuffer.length > 0) {
this.el.innerHTML += this.lineFormatter.toHtml(escapeHTML(this.lineBuffer));
this.lineBuffer = '';
if (this.autoFollow) {
window.scrollTo(0, document.body.scrollHeight);
}
}
if(this.stoppingRefresh) {
this.stoppingRefresh = false;
} else {
window.requestAnimationFrame(this.update.bind(this));
}
},
write: function(data) {
this.lineBuffer += data;
}
};
})();

View file

@ -1,78 +0,0 @@
function UserViewModel() {
var self = this;
// handle requests to create a new user.
$(".modal-user button").click(function(e) {
var login = $(".modal-user input").val();
var user = { login: login };
$(".alert-danger").hide();
$.ajax({
url: "/api/users",
type: "POST",
data: JSON.stringify(user),
contentType: "application/json",
success: function( data ) {
// clears the form value
$(".modal-user input").val("");
// find an existing item in the list and clone it.
var el = $(".col-sm-4").first().clone();
el.find("img").attr("src", data.avatar_url);
el.find("h3").text(data.login);
el.find("p").text(data.email);
el.find(".card").attr("data-id", data.login);
el.find(".card").attr("data-admin", data.admin);
$( ".user-row" ).prepend(el);
},
error: function(data) {
$(".alert-danger").text(data.responseText);
$(".alert-danger").show();
}
});
});
$(".user-row").on('click', '.btn-group .btn-info', function(){
// gets the unique identifier for the click row, which is
// the user login id.
var row = $( this ).closest("[data-id]");
var id = row.data("id");
var admin = row.data("admin") == true;
row.attr("data-admin", !admin);
$.ajax({
url: "/api/users/"+id,
type: "PATCH",
data: JSON.stringify({active:true, admin: !admin}),
contentType: "application/json"
});
});
$(".user-row").on('click', '.btn-group .btn-danger', function(){
// gets the unique identifier for the click row, which is
// the user login id.
var row = $( this ).closest("[data-id]");
var id = row.data("id");
var r = confirm("Are you sure you want to delete "+id+"?");
if (r === false) {
return;
}
// makes an API call to delete the user and then, if successful,
// removes from the list.
$.ajax({
url: "/api/users/"+id,
type: "DELETE",
success: function( data ) {
row.parent().remove();
}
});
});
}

View file

@ -1,15 +0,0 @@
Array.prototype.remove = function() {
var what, a = arguments, L = a.length, ax;
while (L && this.length) {
what = a[--L];
while ((ax = this.indexOf(what)) !== -1) {
this.splice(ax, 1);
}
}
return this;
};
function escapeHTML(html) {
return document.createElement('div').appendChild(
document.createTextNode(html)).parentNode.innerHTML;
}

View file

@ -1,138 +0,0 @@
// Livestamp.js / v2.0.0 / (c) 2015 Matt Bradley / MIT License
(function (plugin) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery', 'moment'], plugin);
} else {
// Browser globals
plugin(jQuery, moment);
}
}(function($, moment) {
var updateInterval = 1e3,
paused = false,
$livestamps = $([]),
init = function() {
livestampGlobal.resume();
},
prep = function($el, timestamp) {
var oldData = $el.data('livestampdata');
if (typeof timestamp == 'number')
timestamp *= 1e3;
$el.removeAttr('data-livestamp')
.removeData('livestamp');
timestamp = moment(timestamp);
if (moment.isMoment(timestamp) && !isNaN(+timestamp)) {
var newData = $.extend({ }, { 'original': $el.contents() }, oldData);
newData.moment = moment(timestamp);
$el.data('livestampdata', newData).empty();
$livestamps.push($el[0]);
}
},
run = function() {
if (paused) return;
livestampGlobal.update();
setTimeout(run, updateInterval);
},
livestampGlobal = {
update: function() {
$('[data-livestamp]').each(function() {
var $this = $(this);
prep($this, $this.data('livestamp'));
});
var toRemove = [];
$livestamps.each(function() {
var $this = $(this),
data = $this.data('livestampdata');
if (data === undefined)
toRemove.push(this);
else if (moment.isMoment(data.moment)) {
var from = $this.html(),
to = data.moment.fromNow();
if (from != to) {
var e = $.Event('change.livestamp');
$this.trigger(e, [from, to]);
if (!e.isDefaultPrevented())
$this.html(to);
}
}
});
$livestamps = $livestamps.not(toRemove);
delete $livestamps.prevObject
},
pause: function() {
paused = true;
},
resume: function() {
paused = false;
run();
},
interval: function(interval) {
if (interval === undefined)
return updateInterval;
updateInterval = interval;
}
},
livestampLocal = {
add: function($el, timestamp) {
if (typeof timestamp == 'number')
timestamp *= 1e3;
timestamp = moment(timestamp);
if (moment.isMoment(timestamp) && !isNaN(+timestamp)) {
$el.each(function() {
prep($(this), timestamp);
});
livestampGlobal.update();
}
return $el;
},
destroy: function($el) {
$livestamps = $livestamps.not($el);
$el.each(function() {
var $this = $(this),
data = $this.data('livestampdata');
if (data === undefined)
return $el;
$this
.html(data.original ? data.original : '')
.removeData('livestampdata');
});
return $el;
},
isLivestamp: function($el) {
return $el.data('livestampdata') !== undefined;
}
};
$.livestamp = livestampGlobal;
$(init);
$.fn.livestamp = function(method, options) {
if (!livestampLocal[method]) {
options = method;
method = 'add';
}
return livestampLocal[method](this, options);
};
}));

View file

@ -1,666 +0,0 @@
var Plates = (typeof module !== 'undefined' && 'id' in module && typeof exports !== 'undefined') ? exports : {};
!function(exports, env, undefined) {
"use strict";
//
// Cache variables to increase lookup speed.
//
var _toString = Object.prototype.toString;
//
// Polyfill the Array#indexOf method for cross browser compatibility.
//
[].indexOf || (Array.prototype.indexOf = function indexOf(a, b ,c){
for (
c = this.length , b = (c+ ~~b) % c;
b < c && (!(b in this) || this[b] !==a );
b++
);
return b^c ? b : -1;
});
//
// Polyfill Array.isArray for cross browser compatibility.
//
Array.isArray || (Array.isArray = function isArray(a) {
return _toString.call(a) === '[object Array]';
});
//
// ### function fetch(data, mapping, value, key)
// #### @data {Object} the data that we need to fetch a value from
// #### @mapping {Object} The iterated mapping step
// #### @tagbody {String} the tagbody we operated against
// #### @key {String} optional key if the mapping doesn't have a dataKey
// Fetches the correct piece of data
//
function fetch(data, mapping, value, tagbody, key) {
key = mapping.dataKey || key;
//
// Check if we have data manipulation or filtering function.
//
if (mapping.dataKey && typeof mapping.dataKey === 'function') {
return mapping.dataKey(data, value || '', tagbody || '', key);
}
//
// See if we are using dot notation style
//
if (!~key.indexOf('.')) return data[key];
var result = key
, structure = data;
for (var paths = key.split('.'), i = 0, length = paths.length; i < length && structure; i++) {
result = structure[+paths[i] || paths[i]];
structure = result;
}
return result !== undefined ? result : data[key];
}
//
// compileMappings
//
// sort the mappings so that mappings for the same attribute and value go consecutive
// and inside those, those that change attributes appear first.
//
function compileMappings(oldMappings) {
var mappings = oldMappings.slice(0);
mappings.sort(function(map1, map2) {
if (!map1.attribute) return 1;
if (!map2.attribute) return -1;
if (map1.attribute !== map2.attribute) {
return map1.attribute < map2.attribute ? -1 : 1;
}
if (map1.value !== map2.value) {
return map1.value < map2.value ? -1 : 1;
}
if (! ('replace' in map1) && ! ('replace' in map2)) {
throw new Error('Conflicting mappings for attribute ' + map1.attribute + ' and value ' + map1.value);
}
if (map1.replace) {
return 1;
}
return -1;
});
return mappings;
}
//
// Matches a closing tag to a open tag
//
function matchClosing(input, tagname, html) {
var closeTag = '</' + tagname + '>',
openTag = new RegExp('< *' + tagname + '( *|>)', 'g'),
closeCount = 0,
openCount = -1,
from, to, chunk
;
from = html.search(input);
to = from;
while(to > -1 && closeCount !== openCount) {
to = html.indexOf(closeTag, to);
if (to > -1) {
to += tagname.length + 3;
closeCount ++;
chunk = html.slice(from, to);
openCount = chunk.match(openTag).length;
}
}
if (to === -1) {
throw new Error('Unmatched tag ' + tagname + ' in ' + html)
}
return chunk;
}
var Merge = function Merge() {};
Merge.prototype = {
nest: [],
tag: new RegExp([
'<',
'(/?)', // 2 - is closing
'([-:\\w]+)', // 3 - name
'((?:[-\\w]+(?:', '=',
'(?:\\w+|["|\'](?:.*)["|\']))?)*)', // 4 - attributes
'(/?)', // 5 - is self-closing
'>'
].join('\\s*')),
//
// HTML attribute parser.
//
attr: /([\-\w]*)\s*=\s*(?:(["\'])([\-\.\w\s\/:;&#]*)\2)/gi,
//
// In HTML5 it's allowed to have to use self closing tags without closing
// separators. So we need to detect these elements based on the tag name.
//
selfClosing: /^(area|base|br|col|command|embed|hr|img|input|keygen|link|meta|param|source|track|wbr)$/,
//
// ### function hasClass(str, className)
// #### @str {String} the class attribute
// #### @className {String} the className that the classAttribute should contain
//
// Helper function for detecting if a class attribute contains the className
//
hasClass: function hasClass(str, className) {
return ~str.split(' ').indexOf(className);
},
//
// ### function iterate(html, value, components, tagname, key)
// #### @html {String} peice of HTML
// #### @value {Mixed} iterateable object with data
// #### @components {Array} result of the this.tag regexp execution
// #### @tagname {String} the name of the tag that we iterate on
// #### @key {String} the key of the data that we need to extract from the value
// #### @map {Object} attribute mappings
//
// Iterate over over the supplied HTML.
//
iterate: function iterate(html, value, components, tagname, key, map) {
var output = '',
segment = matchClosing(components.input, tagname, html),
data = {};
// Is it an array?
if (Array.isArray(value)) {
// Yes: set the output to the result of iterating through the array
for (var i = 0, l = value.length; i < l; i++) {
// If there is a key, then we have a simple object and
// must construct a simple object to use as the data
if (key) {
data[key] = value[i];
} else {
data = value[i];
}
output += this.bind(segment, data, map);
}
return output;
} else if (typeof value === 'object') {
// We need to refine the selection now that we know we're dealing with a
// nested object
segment = segment.slice(components.input.length, -(tagname.length + 3));
return output += this.bind(segment, value, map);
}
return value;
},
//
// ### function bind(html, data, map)
// #### @html {String} the template that we need to modify
// #### @data {Object} data for the template
// #### @map {Mapper} instructions for the data placement in the template
// Process the actual template
//
bind: function bind(html, data, map) {
if (Array.isArray(data)) {
var output = '';
for (var i = 0, l = data.length; i<l; i++) {
output += this.bind(html, data[i], map);
}
return output;
}
html = (html || '').toString();
data = data || {};
var that = this;
var openers = 0,
remove = 0,
components,
attributes,
mappings = map && compileMappings(map.mappings),
intag = false,
tagname = '',
isClosing = false,
isSelfClosing = false,
selfClosing = false,
matchmode = false,
createAttribute = map && map.conf && map.conf.create,
closing,
tagbody;
var c,
buffer = '',
left;
for (var i = 0, l = html.length; i < l; i++) {
c = html.charAt(i);
//
// Figure out which part of the HTML we are currently processing. And if
// we have queued up enough HTML to process it's data.
//
if (c === '!' && intag && !matchmode) {
intag = false;
buffer += html.slice(left, i + 1);
} else if (c === '<' && !intag) {
closing = true;
intag = true;
left = i;
} else if (c === '>' && intag) {
intag = false;
tagbody = html.slice(left, i + 1);
components = this.tag.exec(tagbody);
if(!components) {
intag = true;
continue;
}
isClosing = components[1];
tagname = components[2];
attributes = components[3];
selfClosing = components[4];
isSelfClosing = this.selfClosing.test(tagname);
if (matchmode) {
//
// and its a closing.
//
if (!!isClosing) {
if (openers <= 0) {
matchmode = false;
} else {
--openers;
}
} else if (!isSelfClosing) {
//
// and its not a self-closing tag
//
++openers;
}
}
if (!isClosing && !matchmode) {
//
// if there is a match in progress and
//
if (mappings && mappings.length > 0) {
for (var ii = mappings.length - 1; ii >= 0; ii--) {
var setAttribute = false
, mapping = mappings[ii]
, shouldSetAttribute = mapping.re && attributes.match(mapping.re);
//
// check if we are targetting a element only or attributes
//
if ('tag' in mapping && !this.attr.test(tagbody) && mapping.tag === tagname) {
tagbody = tagbody + fetch(data, mapping, '', tagbody);
continue;
}
tagbody = tagbody.replace(this.attr, function(str, key, q, value, a) {
var newdata;
if (shouldSetAttribute && mapping.replace !== key || remove) {
return str;
} else if (shouldSetAttribute || typeof mapping.replacePartial1 !== 'undefined') {
setAttribute = true;
//
// determine if we should use the replace argument or some value from the data object.
//
if (typeof mapping.replacePartial2 !== 'undefined') {
newdata = value.replace(mapping.replacePartial1, mapping.replacePartial2);
} else if (typeof mapping.replacePartial1 !== 'undefined' && mapping.dataKey) {
newdata = value.replace(mapping.replacePartial1, fetch(data, mapping, value, tagbody, key));
} else {
newdata = fetch(data, mapping, value, tagbody, key);
}
return key + '="' + (newdata || '') + '"';
} else if (!mapping.replace && mapping.attribute === key) {
if (
mapping.value === value ||
that.hasClass(value, mapping.value ||
mappings.conf.where === key) ||
(_toString.call(mapping.value) === '[object RegExp]' &&
mapping.value.exec(value) !== null)
) {
if (mapping.remove) {
//
// only increase the remove counter if it's not a self
// closing element. As matchmode is suffectient to
// remove tose
//
if (!isSelfClosing) remove++;
matchmode = true;
} else if (mapping.plates) {
var partial = that.bind(
mapping.plates
, typeof mapping.data === 'string' ? fetch(data, { dataKey: mapping.data }) : mapping.data || data
, mapping.mapper
);
buffer += tagbody + that.iterate(html, partial, components, tagname, undefined, map);
matchmode = true;
} else {
var v = newdata = fetch(data, mapping, value, tagbody, key);
newdata = tagbody + newdata;
if (Array.isArray(v)) {
newdata = that.iterate(html, v, components, tagname, value, map);
// If the item is an array, then we need to tell
// Plates that we're dealing with nests
that.nest.push(tagname);
} else if (typeof v === 'object') {
newdata = tagbody + that.iterate(html, v, components, tagname, value, map);
}
buffer += newdata || '';
matchmode = true;
}
}
}
return str;
});
//
// Do we need to create the attributes if they don't exist.
//
if (createAttribute && shouldSetAttribute && !setAttribute) {
var spliced = selfClosing ? 2 : 1
, close = selfClosing ? '/>': '>'
, left = tagbody.substr(0, tagbody.length - spliced);
if (left[left.length - 1] === ' ') {
left = left.substr(0, left.length - 1);
if (selfClosing) {
close = ' ' + close;
}
}
tagbody = [
left,
' ',
mapping.replace,
'="',
fetch(data, mapping),
'"',
close
].join('');
}
}
} else {
//
// if there is no map, we are just looking to match
// the specified id to a data key in the data object.
//
tagbody.replace(this.attr, function (attr, key, q, value, idx) {
if (key === map && map.conf.where || 'id' && data[value]) {
var v = data[value],
nest = Array.isArray(v),
output = nest || typeof v === 'object'
? that.iterate(html.substr(left), v, components, tagname, value, map)
: v;
// If the item is an array, then we need to tell
// Plates that we're dealing with nests
if (nest) { that.nest.push(tagname); }
buffer += nest ? output : tagbody + output;
matchmode = true;
}
});
}
}
//
// if there is currently no match in progress
// just write the tagbody to the buffer.
//
if (!matchmode && that.nest.length === 0) {
if (!remove) buffer += tagbody;
if (remove && !!isClosing) --remove;
} else if (!matchmode && that.nest.length) {
this.nest.pop();
}
} else if (!intag && !matchmode) {
//
// currently not inside a tag and there is no
// match in progress, we can write the char to
// the buffer.
//
if (!remove) buffer += c;
}
}
return buffer;
}
};
//
// ### function Mapper(conf)
// #### @conf {Object} configuration object
// Constructor function for the Mapper instance that is responsible for
// providing the mapping for the data structure
//
function Mapper(conf) {
if (!(this instanceof Mapper)) { return new Mapper(conf); }
this.mappings = [];
this.conf = conf || {};
}
//
// ### function last(newitem)
// #### @newitem {Boolean} do we need to add a new item to the mapping
// Helper function for adding new attribute maps to a Mapper instance
//
function last(newitem) {
if (newitem) {
this.mappings.push({});
}
var m = this.mappings[this.mappings.length - 1];
if (m && m.attribute && m.value && m.dataKey && m.replace) {
m.re = new RegExp(m.attribute + '=([\'"]?)' + m.value + '\\1');
} else if (m) {
delete m.re;
}
return m;
}
//
// Create the actual chainable methods: where('class').is('foo').insert('bla')
//
Mapper.prototype = {
//
// ### function replace(val1, val2)
// #### @val1 {String|RegExp} The part of the attribute that needs to be replaced
// #### @val2 {String} The value it should be replaced with
//
replace: function replace(val1, val2) {
var l = last.call(this);
l.replacePartial1 = val1;
l.replacePartial2 = val2;
return this;
},
//
// ### function use(val)
// #### @val {String} A string that represents a key.
// Data will be inserted into the attribute that was specified in the
// `where` clause.
//
use: function use(val) {
last.call(this).dataKey = val;
return last.call(this) && this;
},
//
// ### function where(val)
// #### @val {String} an attribute that may be found in a tag
// This method will initiate a clause. Once a clause has been established
// other member methods will be chained to each other in any order.
//
where: function where(val) {
last.call(this, true).attribute = val;
return last.call(this) && this;
},
//
// ### function class(val)
// #### @val {String} a value that may be found in the `class` attribute of a tag
// the method name should be wrapped in quotes or it will throw errors in IE.
//
'class': function className(val) {
return this.where('class').is(val);
},
//
// ### function tag(val)
// #### @val {String} the name of the tag should be found
//
tag: function tag(val) {
last.call(this, true).tag = val;
return this;
},
//
// ### function is(val)
// #### @val {string} The value of the attribute that was specified in the
// `where` clause.
//
is: function is(val) {
last.call(this).value = val;
return last.call(this) && this;
},
//
// ### function has(val)
// #### @val {String|RegExp} The value of the attribute that was specified
// in the `where` clause.
//
has: function has(val) {
last.call(this).value = val;
this.replace(val);
return last.call(this) && this;
},
//
// ### function insert(val)
// #### @val {String} A string that represents a key. Data will be inserted
// in to the attribute that was specified in the `where` clause.
//
insert: function insert(val) {
var l = last.call(this);
l.replace = l.attribute;
l.dataKey = val;
return last.call(this) && this;
},
//
// ### function as(val)
// #### @val {String} A string that represents an attribute in the tag.
// If there is no attribute by that name name found, one may be created
// depending on the options that where passed in the `Plates.Map`
// constructor.
//
as: function as(val) {
last.call(this).replace = val;
return last.call(this) && this;
},
//
// ### function remove()
// This will remove the element that was specified in the `where` clause
// from the template.
//
remove: function remove() {
last.call(this).remove = true;
return last.call(this, true);
},
//
// ### function append(plates, data, map)
// #### @plates {String} Template or path/id of the template
// #### @data {Object|String} data for the appended template
// #### @map {Plates.Map} mapping for the data
//
append: function append(plates, data, map) {
var l = last.call(this);
if (data instanceof Mapper) {
map = data;
data = undefined;
}
// If the supplied plates template doesn't contain any HTML it's most
// likely that we need to import it. To improve performance we will cache
// the result of the file system.
if (!/<[^<]+?>/.test(plates) && !exports.cache[plates]) {
// figure out if we are running in Node.js or a browser
if ('document' in env && 'getElementById' in env.document) {
exports.cache[plates] = document.getElementById(plates).innerHTML;
} else {
exports.cache[plates] = require('fs').readFileSync(
require('path').join(process.cwd(), plates),
'utf8'
);
}
}
l.plates = exports.cache[plates] || plates;
l.data = data;
l.mapper = map;
return last.call(this, true);
}
};
//
// Provide helpful aliases that well help with increased compatibility as not
// all browsers allow the Mapper#class prototype (IE).
//
Mapper.prototype.className = Mapper.prototype['class'];
//
// Aliases of different methods.
//
Mapper.prototype.partial = Mapper.prototype.append;
Mapper.prototype.to = Mapper.prototype.use;
//
// Expose a simple cache object so people can clear the cached partials if
// they want to.
//
exports.cache = {};
//
// Expose the Plates#bind interface.
//
exports.bind = function bind(html, data, map) {
var merge = new Merge();
return merge.bind(html, data, map);
};
//
// Expose the Mapper.
//
exports.Map = Mapper;
}(Plates, this);

View file

@ -1,27 +0,0 @@
package static
import (
"net/http"
"github.com/elazarl/go-bindata-assetfs"
)
//go:generate go run ../contrib/generate-js.go -dir scripts/ -o scripts_gen/drone.min.js
//go:generate sassc --style compact styles/style.sass styles_gen/style.css
//go:generate go-bindata-assetfs -ignore "\\.go" -pkg static -o static_gen.go ./...
func FileSystem() http.FileSystem {
fs := &assetfs.AssetFS{Asset: Asset, AssetDir: AssetDir, Prefix: ""}
return &binaryFileSystem{
fs,
}
}
type binaryFileSystem struct {
fs http.FileSystem
}
func (b *binaryFileSystem) Open(name string) (http.File, error) {
return b.fs.Open(name[1:])
}

View file

@ -1,2 +0,0 @@

View file

@ -1,56 +0,0 @@
.navbar
background-color: #FFF;
border-radius: 0px;
height: 53px;
z-index: 2;
margin-top: 10px;
.navbar-brand
background-image: url(/static/images/logo_dark.svg);
width: 30px;
height: 30px;
margin-top: 3px;
background-size: 30px;
background-repeat: no-repeat;
background-position: center center;
.navbar img
width: 32px;
height: 32px;
border-radius: 50%;
.navbar .dropdown
display: inline-block;
button
background: none;
border: none;
color: #2b303b;
min-wdith: 24px;
max-width: 24px;
.material-icons
line-height: 32px;
min-wdith: 24px;
max-width: 24px;
.navbar-nav.navbar-right
display: inline-block;
float: right;
li
display: inline-block;
vertical-align: middle;
.navbar-form
margin-right: 20px;
width: 350px;
.twitter-typeahead
width: 100%;
.form-control
color: #747C84;
border: none;
background-color: #eff1f5;
border-radius: 0px;
padding: 0.3rem 0.75rem;
width: 100%;

View file

@ -1,95 +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;

View file

@ -1,46 +0,0 @@
.success,
.failure,
.killed,
.error,
.running,
.pending
padding: 0px 15px;
color: #FFF;
width: 100px;
text-align: center;
border-radius: 2px;
text-transform: uppercase;
font-size: 11px;
line-height: 22px;
display: inline-block;
margin-right: 10px;
.error,
.killed,
.failure
background: #bf616a;
.success
background: #a3be8c;
.pending,
.running
background: #ebcb8b;
animation: horizontal 2s ease infinite;
@keyframes horizontal
0%
transform: translate(0,0)
6%
transform: translate(5px,0)
12%
transform: translate(0,0)
18%
transform: translate(5px,0)
24%
transform: translate(0,0)
30%
transform: translate(5px,0)
36%,100%
transform: translate(0,0)

View file

@ -1,95 +0,0 @@
.subnav
background: #FFF;
border-bottom: 1px solid #EEE;
height: 75px;
min-height: 75px;
max-height: 75px;
margin-bottom: 40px;
position: relative;
ol
float: left;
margin: 0px;
margin-left: 20px;
padding: 0px;
li
line-height: 75px;
display: inline-block;
vertical-align: middle;
font-size: 21px;
.btn
background: transparent
outline: none;
.btn:focus
outline: none;
.btn-info
cursor: pointer;
width: auto;
text-transform: uppercase;
padding: 0px 10px;
border-radius: 2px;
font-size: 11px;
line-height: 30px;
height: auto;
margin-left: 10px;
border-color: #95AEC7;
color: #95AEC7;
li.separator i
vertical-align: middle
li a
color: #2B303B;
line-height: 75px;
text-decoration: none;
&:hover
text-decoration: none;
.nav-tabs
position: absolute;
bottom: -1px;
right: 30px;
border-bottom: 1px solid #eee;
.nav-tabs .nav-link
border: none;
color: #C7CCD1;
font-size: 13px;
padding: 10px 20px;
text-transform: uppercase;
.nav-tabs .nav-link.active
border: 1px solid #eee;
border-bottom: 1px solid #fff;
color: #2b303b;
// .nav-tabs
// position:absolute;
// bottom:-1px;
// right:30px;
// border-bottom:1px solid #eee;
//
// .nav-tabs .nav-link
// border:none;
// color: #C7CCD1;
// font-size:13px;
// padding:10px 0px;
// margin-left:10px;
// margin-right:10px;
// text-transform:uppercase;
//
// .nav-tabs .nav-item:last-child .nav-link
// margin-right:5px;
//
// .nav-tabs .nav-link.active
// border: none;
// border-bottom:2px solid #2b303b;
// color: #2b303b;

View file

@ -1,43 +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);

View file

@ -1,86 +0,0 @@
.timeline
padding-left: 50px;
position: relative;
margin-top: 10px;
margin-bottom: 40px;
.card
display: flex;
border: none;
border-radius: 0px;
border-top: 1px solid #eceeef;
text-decoration: none;
color: #2b303b;
.card-header
background: #FFF;
border: none;
padding: 0px;
width: 50px;
min-width: 50px;
max-width: 50px;
padding-top: 30px;
.card-block
flex: 1 1 auto;
padding: 30px 12px 12px 12px;
p
color: #ADB3BA;
margin-top: 2px;
font-size: 0px;
em
text-decoration: none;
color: #747C84;
font-style: normal;
font-size: 0.9rem;
span
margin: 0px 5px;
font-size: 0.9rem;
h3
display: inline-block;
line-height: 22px;
font-size: 18px;
.card:nth-child(2)
border-top: 0px;
.timeline:before
position: absolute;
top: 0;
bottom: 0;
left: 14px;
z-index: -1;
display: block;
width: 1px;
content: "";
background-color: #DFE2E5;
.group
padding-bottom: 16px;
.group-title
font-size: 13px;
text-transform: uppercase;
color: #ADB3BA;
margin-bottom: 30px;
margin-top: 30px;
position: relative;
line-height: 20px;
.group-title:before
content: '\f01f';
font-family: "octicons";
position: absolute;
left: -48px;
font-size: 20px;
background: #FFF;
width: 24px;
vertical-align: middle;
text-align: center;
color: #ADB3BA;
.group:first-child .group-title
margin-top: 0px;
.group:last-child
padding-bottom: 0px;

View file

@ -1,195 +0,0 @@
// this is the build output section of the page that displays
// the ansi terminal output.
#output
background: #0d1926;
color: #d9e6f2;
margin-right: 15px;
font-size: 13px;
color: #eff1f5;
border-radius: 2px;
background: #2b303b;
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
padding: 35px 40px;
font-family: "Roboto Mono";
min-height: calc(100vh - 205px);
#output > pre
font-size: 13px;
color: #eff1f5;
border-radius: 2px;
background: #2b303b;
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
padding: 0px;
font-family: "Roboto Mono";
margin: 0px;
margin-top: 25px;
#output > pre:first-child
margin-top: 0px;
#output > pre:before
content: attr(data-title);
display: block;
padding: 5px;
margin-bottom: 10px;
border-bottom: 1px solid rgba(255,255,255,0.3);
padding-left: 0px;
#follow
position: absolute;
.build-summary
padding-left: 20px;
p
color: #ADB3BA;
margin-top: 4px;
font-size: 0.9rem;
em
color: #747C84;
text-decoration: none;
font-style: normal;
// .success,
// .failure,
// .killed,
// .error,
// .running,
// .pending
// font-size: 15px;
// padding: 2px 28px;
// width: auto;
.job-summary
padding-left: 20px;
font-size: 0.9rem;
dt
color: #ADB3BA;
font-weight: normal;
dd
color: #747C84;
text-decoration: none;
font-style: normal;
.build-summary > div
h3
display: inline;
line-height: 28px;
font-size: 18px;
a.material-icons
vertical-align: middle;
text-decoration: none;
color: #ADB3BA;
font-size: 20px;
padding-left: 5px;
.job-list
padding-left: 20px;
margin-bottom: 20px;
.job-list a
h3
margin-bottom: 10px;
color: #2b303b;
font-size: 14px;
margin-top: 14px;
.env
font-family: "Roboto Mono"
text-decoration: none;
color: #747C84;
font-size: 15px;
display: block;
border-top: 1px solid #eee;
padding-top: 20px;
padding-bottom: 20px;
position: relative;
&:not(.active) > div:nth-child(2) > div
display: none
// &:not(.active) > div:nth-child(2) > h3
// color:#ADB3BA;
// &:not(.active) > div:nth-child(1):after
// content: "\2026";
// display:block;
// padding:0px 10px;
// vertical-align:middle;
// border:1px solid #eee;
// background:#fff;
// position:absolute;
// right:0px;
// top:0px;
// height: 20px;
// line-height: 10px;
.job-list a:last-child
border-bottom: 1px solid #eee;
.job-list a > div:first-child
margin-bottom: 10px;
position: relative;
//.job-list a.active:after
// content: "";
// border-left: 2px solid #eee;
// position: absolute;
// top: 0px;
// left: -35px;
// bottom: 0px;
// width: 10px;
.build-btn-group
margin-left: 20px;
.btn
background: #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-right: 10px;
.btn-danger
border: 1px solid #bf616a;
color: #bf616a;
&.btn-info
border-color: #95AEC7;
color: #95AEC7;
.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;
border: none;
outline: none;
display: none;
.tail i
color: rgba(255,255,255,0.5);
line-height: 38px;
display: inline-block;
@supports (position:sticky)
.sticky
position: sticky;
top: 20px;
@supports not (position:sticky)
.sticky
top: 0px;

View file

@ -1,331 +0,0 @@
.toc
list-style-type: none;
padding: 0px;
margin: 0px;
padding-bottom: 40px;
h2
font-size: 21px;
font-weight: normal;
margin-bottom: 20px;
color: #2b303b;
ul
list-style-type: none;
padding: 0px;
margin: 0px;
margin-bottom: 40px;
border-bottom: 1px solid #EEE;
padding-bottom: 40px;
li
a
color: #2b303b;
text-decoration: none;
a:hover
text-decoration: underline;
[data-method]:before
content: attr(data-method);
padding: 0px 10px;
line-height: 18px;
min-width: 70px;
font-size: 11px;
text-transform: uppercase;
display: inline-block;
text-align: center;
color: #FFF;
border-radius: 2px;
margin-right: 20px;
[data-method="GET"]:before
background-color: #1ABC9C;
[data-method="PUT"]:before
background-color: #9B59B6;
[data-method="POST"]:before
background-color: #3498DB;
[data-method="PATCH"]:before
background-color: #E67E22;
[data-method="DELETE"]:before
background-color: #E74C3C;
[data-method]:before
background: #FFF;
border: 1px solid #FFF;
[data-method="GET"]:before
color: #1ABC9C;
border-color: #1ABC9C;
[data-method="PUT"]:before
color: #9B59B6;
border-color: #9B59B6;
[data-method="POST"]:before
color: #3498DB;
border-color: #3498DB;
[data-method="PATCH"]:before
color: #E67E22;
border-color: #E67E22;
[data-method="DELETE"]:before
color: #E74C3C;
border-color: #E74C3C;
.operation
[data-method]:before
content: attr(data-method);
padding: 0px 10px;
line-height: 18px;
min-width: 70px;
font-size: 11px;
text-transform: uppercase;
display: inline-block;
text-align: center;
color: #FFF;
border-radius: 2px;
margin-right: 20px;
background: #FFF;
border: 1px solid #FFF;
line-height: 20px;
vertical-align: top;
[data-method]:before
background: #FFF;
border: 1px solid #FFF;
[data-method="GET"]:before
color: #1ABC9C;
border-color: #1ABC9C;
[data-method="PUT"]:before
color: #9B59B6;
border-color: #9B59B6;
[data-method="POST"]:before
color: #3498DB;
border-color: #3498DB;
[data-method="PATCH"]:before
color: #E67E22;
border-color: #E67E22;
[data-method="DELETE"]:before
color: #E74C3C;
border-color: #E74C3C;
.docs
margin-top: 40px;
padding: 0px 50px;
padding-right: 40px;
.docs-api
pre
margin-right: 15px;
font-size: 13px;
color: #eff1f5;
color: #2b303b;
border-radius: 2px;
background: #2b303b;
background: #ECF0F1;
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
padding: 25px 30px;
font-family: "Roboto Mono";
margin-right: 0px;
padding-left: 40px;
background: #eff1f5;
color: #2b303b;
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
padding: 25px 30px;
font-family: "Roboto Mono";
border-radius: 2px;
font-size: 13px;
.operation
min-height: 100vh;
padding: 20px 0px;
display: flex
&> aside,
&> div
min-width: 50%;
max-width: 50%;
width: 0%;
padding-right: 40px;
h2
color: #2b303b;
font-size: 21px;
h3
font-size: 16px;
margin-bottom: 20px;
margin-top: 40px;
aside
background: rgba(43, 48, 59, 0.95);
box-sizing: border-box;
padding: 20px 0px 10px 0px;
border-radius: 2px;
h4
color: #d0d4d7;
font-size: 15px;
padding: 20px;
padding-left: 40px;
pre
background: #2b303b;
color: #d0d4d7;
margin-right: 0px;
padding-left: 40px;
.params
padding: 0px;
margin: 0px;
list-style-type: none;
border-top: 1px solid #f0f4f7
li
padding: 15px 10px;
border-bottom: 1px solid #f0f4f7;
font-size: 15px
p
line-height: 20px;
margin: 0 0 0 170px;
li:after
visibility: hidden;
display: block;
font-size: 0;
content: " ";
clear: both;
height: 0;
h4
float: left;
line-height: 20px;
text-align: right;
padding-right: 2 0px;
width: 150px;
font-weight: bold;
font-size: 15px;
small
display: block;
text-transform: uppercase;
color: #E67E22;
font-size: 11px;
font-weight: normal;
margin-top: 5px;
.operation
aside
background: rgba(239, 241, 245, 0.49)
h4
color: #2b303b
pre
background: #eff1f5
color: #2b303b
.docs-usage
.row
display: flex;
.content-nav
max-width: 250px;
min-width: 250px;
width: 250px;
ul
padding: 0px;
margin: 0px;
list-style-type: none;
li
position: relative;
color: rgba(0,0,0,0.5);
text-transform: uppercase;
font-size: 13px;
li a
color: rgba(43, 48, 59, 0.8);
line-height: 30px;
display: block;
font-size: 16px;
text-transform: none;
li.active a
color: #2b303b;
font-size: 16px;
text-transform: none;
li.active:before
content: "";
border-left: 4px solid #2b303b;
position: absolute;
top: 0px;
bottom: 0px;
left: -20px;
ul
margin-bottom: 25px;
margin-top: 5px;
.content-main
max-width: 900px;
margin: 0px auto;
margin-bottom: 40px;
font-size: 14px;
p
line-height: 20px;
margin: 20px 0px;
blockquote
color: #31708f;
background-color: #d9edf7;
border-color: #bcdff1;
padding: 30px;
margin: 15px 0px;
border-radius: 2px;
blockquote p:first-child
margin-top: 0px;
blockquote p:last-child
margin-bottom: 0px;
strong
font-weight: bold;
h1
margin: 40px 0px 20px 0px;
font-size: 22px;
h1:first-child
margin-top: 0px;
h2
margin: 40px 0px 30px 0px;
font-size: 20px;
border-top: 1px solid #EEE;
padding-top: 30px;
h3
margin: 40px 0px 20px 0px;
font-size: 16px;
ul code, ol code, p code
background: #eff1f5;
color: #2b303b;
padding: 0px 7px;
font-family: "Roboto Mono";
font-size: 13px;
white-space: nowrap;
pre
margin-right: 0px;
padding-left: 40px;
background: #eff1f5;
color: #2b303b;
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
padding: 25px 30px;
font-family: "Roboto Mono";
border-radius: 2px;
font-size: 13px;
pre > code
background: #eff1f5;
color: #2b303b;
font-family: "Roboto Mono";
font-size: 13px;

View file

@ -1,57 +0,0 @@
.repo-row
.col-sm-4
width: 100%;
.col-sm-4:last-child .card
border-bottom: none
.card
border: none;
border-bottom: 1px solid #EEE;
border-radius: 0px;
position: relative
display: flex;
text-decoration: none;
.card-block
h3
font-size: 1.1rem;
color: #2b303b;
.card-text
color: rgba(0,0,0,0.5);
font-size: 0.95rem;
margin: 0px;
.btn-group
position: absolute;
top: 15px;
right: 0px;
.btn-danger
color: #bf616a;
background: #FFF;
border: 1px solid #bf616a;
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;
.card-header
background: #FFF;
border-bottom: none;
padding-right: 0px;
padding-left: 0px;
width: 45px;
.repo-search
color: #747C84;
border: none;
background-color: #eff1f5;
border-radius: 0px;
padding: 9px 15px;
width: 100%;
margin-bottom: 45px;
border-radius: 2px;

View file

@ -1,120 +0,0 @@
body.login
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
-ms-flex-pack: center;
justify-content: center;
width: 100%;
height: 100%;
overflow: hidden;
div
position: relative;
width: 220px;
height: 30px;
div.logo
background-image: url(/static/images/logo_dark.svg);
background-size: 35px;
background-repeat: no-repeat;
background-position: center center;
position: absolute;
top: 0px;
left: 0px;
height: 30px;
width: 35px;
animation-name: fadein;
animation-duration: 1.5s;
animation-timing-function: ease-in;
input[type="submit"],
a
background: #2b303b;
color: #FFF;
text-decoration: none;
position: absolute;
top: 0px;
right: 0px;
text-transform: uppercase;
font-size: 13px;
min-width: 150px;
text-align: center;
padding: 5px;
border: none;
animation-name: fadein;
animation-duration: 1.5s;
animation-timing-function: ease-in;
div.alert
position: fixed;
top: 0px;
left: 0px;
right: 0px;
line-height: 25px;
padding: 20px;
width: 100%;
border: none;
text-align: center;
vertical-align: middle;
height: auto;
border-radius: 0px;
body.login.login-form
&> div
width: 300px;
height: 100px;
display: flex;
div.logo
width: 65px;
position: relative;
background-position: left center;
animation-name: none;
form
flex: 1 1 auto;
input[type="text"],
input[type="password"]
display: block;
width: 100%;
padding: 5px 10px;
background-color: #eff1f5;
border: none;
color: #747C84;
input[type="password"]
margin-top: 1px;
input[type="submit"]
position: relative;
width: 100%;
margin-top: 20px;
animation-name: none;
@keyframes flyin
0%
left: -3000px;
100%
left: 0px;
@keyframes flyin_right
0%
right: -3000px;
100%
right: 0px;
@keyframes fadein
0%
opacity: 0;
100%
opacity: 1;

View file

@ -1,101 +0,0 @@
.container.repo_config,
.container.repo_secrets
max-width: 800px !important;
.row
border-bottom: 1px solid #f0f4f7;
.row:last-child
border: none;
margin-bottom: 30px;
.col-md-12
padding: 0px;
.row:nth-last-child(2)
border: none;
.col-md-3
padding: 30px;
padding-left: 0px;
max-width: 200px;
font-size: 15px;
color: #2b303b;
.col-md-9
padding: 30px;
color: #65737e;
font-size: 15px;
.btn.btn-info
border-color: #95AEC7;
color: #95AEC7;
background: #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;
pre
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
font-family: "Roboto Mono";
color: #2b303b;
input[type="range"] + span
margin-left: 15px;
.alert.alert-danger
background: #FFF;
border: 1px solid #bf616a;
border-radius: 2px;
margin-top: 40px;
color: #bf616a;
.btn.btn-danger
margin-right: 15px;
border: 1px solid #bf616a;
color: #bf616a;
background: #FFF;
border-radius: 2px;
font-size: 13px;
text-transform: uppercase;
.container.repo_secrets
textarea
border: none;
background: #eff1f5;
width: 100%;
border-radius: 3px;
margin-bottom: 10px;
height: 150px;
padding: 10px 15px;
.btn.btn-info
margin-left: 0px;
.result
margin-top: 20px;
white-space: pre;
.container.repo_activate
max-width: 800px !important;
.alert.alert-info
color: #3498DB;
background: rgba(52, 152, 219, 0.12);
border-radius: 2px;
border: none;
margin-top: 20px;
padding: 30px;
.btn.btn-info
color: #3498DB;
background: #3498DB;
border: 1px solid #3498DB;
background: #FFF;
border-radius: 2px;
text-transform: uppercase;
font-size: 13px;

View file

@ -1,44 +0,0 @@
.container.profile
max-width: 800px !important;
margin-bottom: 40px;
.row
border-bottom: 1px solid #f0f4f7;
.row:last-child
border-bottom: none;
.col-md-3
padding: 30px;
padding-left: 0px;
max-width: 200px;
font-size: 15px;
color: #2b303b;
.col-md-9
padding: 30px;
color: #65737e;
font-size: 15px;
.btn.btn-info
border-color: #95AEC7;
color: #95AEC7;
background: #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;
pre
white-space: pre-wrap;
word-wrap: break-word;
box-sizing: border-box;
font-family: "Roboto Mono";
color: #2b303b;

View file

@ -1,71 +0,0 @@
.user-row
.col-sm-4
width: 100%;
.col-sm-4:last-child .card
border-bottom: none
.card
border: none;
border-bottom: 1px solid #EEE;
border-radius: 0px;
position: relative
display: flex;
.card-block
h3
font-size: 1.1rem;
.card-text
color: rgba(0,0,0,0.5);
font-size: 0.95rem;
margin: 0px;
.btn-group
position: absolute;
top: 15px;
right: 0px;
.btn
background: #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;
.btn-danger
border: 1px solid #bf616a;
color: #bf616a;
.btn-info
border-color: #95AEC7;
color: #95AEC7;
.card-header
background: #FFF;
border-bottom: none;
padding-right: 0px;
padding-left: 0px;
width: 45px;
.card-header
img
vertical-align: middle;
width: 32px;
height: 32px;
border-radius: 3px;
[data-admin="true"] h3:after
content: "admin";
text-transform: uppercase;
color: #fff;
display: inline-block;
font-size: 12px;
margin-left: 15px;
background: #a3be8c;
border-radius: 3px;
padding: 2px 5px;

View file

@ -1,56 +0,0 @@
.tt-open
position: absolute;
top: 34px;
left: 0px;
z-index: 100;
display: none;
background: #FFF;
min-width: 100%;
border: 1px solid #eee;
border-radius: 0px;
.tt-selectable:hover,
.tt-cursor
background: #eff1f5;
.tt-selectable
padding: 1rem 0.75rem;
cursor: pointer;
display: flex;
div:first-child
width: 75px;
max-width: 75px;
min-width: 75px;
img
width: 32px;
max-width: 32px;
min-width: 32px;
border-radius: 50%;
text-align: left;
margin-left: 1rem;
div:last-child
flex: 1 1 auto;
line-height: 32px;
.no-matches-message
padding: 20px;
color: rgba(0,0,0,0.5);
font-style: italic;
.not-indexed-message
padding: 20px;
padding-bottom: 0px;
color: rgba(0,0,0,0.5);
font-style: italic;
em
color: #2b303b;
p:last-child
margin-top: 20px;
font-size: 15px;
color: rgba(0, 0, 0, 0.4);
a
color: rgba(0, 0, 0, 0.8);

View file

@ -1,108 +0,0 @@
@import modules/badges.sass
@import modules/navbar.sass
@import modules/subnav.sass
@import modules/switch.sass
@import modules/range.sass
@import modules/timeline.sass
@import modules/status.sass
@import pages/build.sass
@import pages/users.sass
@import pages/user.sass
@import pages/repo.sass
@import pages/login.sass
@import pages/feed.sass
@import pages/docs.sass
@import header
@import search
.hidden
display: none;
// page navigation
:focus
outline: none;
::-moz-focus-inner
border: 0;
.container
max-width: 980px !important;
.modal-content
padding: 20px;
.form-control
border-radius: 2px;
.btn
margin-top: 20px;
// node section
.node-row
.col-sm-4
width: 100%;
.col-sm-4:last-child .card
border-bottom: none
.card
border: none;
border-bottom: 1px solid #EEE;
border-radius: 0px;
position: relative
display: flex;
.card-block
h3
font-size: 1.1rem;
.card-text
color: rgba(0,0,0,0.5);
font-size: 0.95rem;
margin: 0px;
.btn-group
position: absolute;
top: 15px;
right: 0px;
.btn-danger
color: #bf616a;
background: #FFF;
border: 1px solid #bf616a;
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;
.card-header
background: #FFF;
border-bottom: none;
padding-right: 0px;
padding-left: 0px;
width: 45px;
i.linux_amd64
width: 32px;
height: 32px;
display: inline-block;
background: url(/static/images/ubuntu.svg);
background-size: 32px;
background-repeat: no-repeat;

View file

@ -1,492 +0,0 @@
.navbar { background-color: #FFF; border-radius: 0px; height: 53px; z-index: 2; margin-top: 10px; }
.navbar-brand { background-image: url(/static/images/logo_dark.svg); width: 30px; height: 30px; margin-top: 3px; background-size: 30px; background-repeat: no-repeat; background-position: center center; }
.navbar img { width: 32px; height: 32px; border-radius: 50%; }
.navbar .dropdown { display: inline-block; }
.navbar .dropdown button { background: none; border: none; color: #2b303b; min-wdith: 24px; max-width: 24px; }
.navbar .dropdown button .material-icons { line-height: 32px; min-wdith: 24px; max-width: 24px; }
.navbar-nav.navbar-right { display: inline-block; float: right; }
.navbar-nav.navbar-right li { display: inline-block; vertical-align: middle; }
.navbar-form { margin-right: 20px; width: 350px; }
.navbar-form .twitter-typeahead { width: 100%; }
.navbar-form .form-control { color: #747C84; border: none; background-color: #eff1f5; border-radius: 0px; padding: 0.3rem 0.75rem; width: 100%; }
.subnav { background: #FFF; border-bottom: 1px solid #EEE; height: 75px; min-height: 75px; max-height: 75px; margin-bottom: 40px; position: relative; }
.subnav ol { float: left; margin: 0px; margin-left: 20px; padding: 0px; }
.subnav ol li { line-height: 75px; display: inline-block; vertical-align: middle; font-size: 21px; }
.subnav ol li .btn { background: transparent; outline: none; }
.subnav ol li .btn:focus { outline: none; }
.subnav ol li .btn-info { cursor: pointer; width: auto; text-transform: uppercase; padding: 0px 10px; border-radius: 2px; font-size: 11px; line-height: 30px; height: auto; margin-left: 10px; border-color: #95AEC7; color: #95AEC7; }
.subnav ol li.separator i { vertical-align: middle; }
.subnav ol li a { color: #2B303B; line-height: 75px; text-decoration: none; }
.subnav ol li a:hover { text-decoration: none; }
.subnav .nav-tabs { position: absolute; bottom: -1px; right: 30px; border-bottom: 1px solid #eee; }
.subnav .nav-tabs .nav-link { border: none; color: #C7CCD1; font-size: 13px; padding: 10px 20px; text-transform: uppercase; }
.subnav .nav-tabs .nav-link.active { border: 1px solid #eee; border-bottom: 1px solid #fff; color: #2b303b; }
.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.41667px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 0, 0, 0.1); }
switch:active::before { box-shadow: 0 2px 10.41667px 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.41667px rgba(0, 0, 0, 0.28), 0 0 0 25px rgba(0, 150, 136, 0.2); }
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; }
.timeline { padding-left: 50px; position: relative; margin-top: 10px; margin-bottom: 40px; }
.timeline .card { display: flex; border: none; border-radius: 0px; border-top: 1px solid #eceeef; text-decoration: none; color: #2b303b; }
.timeline .card .card-header { background: #FFF; border: none; padding: 0px; width: 50px; min-width: 50px; max-width: 50px; padding-top: 30px; }
.timeline .card .card-block { flex: 1 1 auto; padding: 30px 12px 12px 12px; }
.timeline .card .card-block p { color: #ADB3BA; margin-top: 2px; font-size: 0px; }
.timeline .card .card-block p em { text-decoration: none; color: #747C84; font-style: normal; font-size: 0.9rem; }
.timeline .card .card-block p span { margin: 0px 5px; font-size: 0.9rem; }
.timeline .card .card-block h3 { display: inline-block; line-height: 22px; font-size: 18px; }
.timeline .card:nth-child(2) { border-top: 0px; }
.timeline:before { position: absolute; top: 0; bottom: 0; left: 14px; z-index: -1; display: block; width: 1px; content: ""; background-color: #DFE2E5; }
.group { padding-bottom: 16px; }
.group-title { font-size: 13px; text-transform: uppercase; color: #ADB3BA; margin-bottom: 30px; margin-top: 30px; position: relative; line-height: 20px; }
.group-title:before { content: '\f01f'; font-family: "octicons"; position: absolute; left: -48px; font-size: 20px; background: #FFF; width: 24px; vertical-align: middle; text-align: center; color: #ADB3BA; }
.group:first-child .group-title { margin-top: 0px; }
.group:last-child { padding-bottom: 0px; }
.success, .failure, .killed, .error, .running, .pending { padding: 0px 15px; color: #FFF; width: 100px; text-align: center; border-radius: 2px; text-transform: uppercase; font-size: 11px; line-height: 22px; display: inline-block; margin-right: 10px; }
.error, .killed, .failure { background: #bf616a; }
.success { background: #a3be8c; }
.pending, .running { background: #ebcb8b; animation: horizontal 2s ease infinite; }
@keyframes horizontal { 0% { transform: translate(0, 0); }
6% { transform: translate(5px, 0); }
12% { transform: translate(0, 0); }
18% { transform: translate(5px, 0); }
24% { transform: translate(0, 0); }
30% { transform: translate(5px, 0); }
36%, 100% { transform: translate(0, 0); } }
#output { background: #0d1926; color: #d9e6f2; margin-right: 15px; font-size: 13px; color: #eff1f5; border-radius: 2px; background: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 35px 40px; font-family: "Roboto Mono"; min-height: calc(100vh - 205px); }
#output > pre { font-size: 13px; color: #eff1f5; border-radius: 2px; background: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 0px; font-family: "Roboto Mono"; margin: 0px; margin-top: 25px; }
#output > pre:first-child { margin-top: 0px; }
#output > pre:before { content: attr(data-title); display: block; padding: 5px; margin-bottom: 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.3); padding-left: 0px; }
#follow { position: absolute; }
.build-summary { padding-left: 20px; }
.build-summary p { color: #ADB3BA; margin-top: 4px; font-size: 0.9rem; }
.build-summary em { color: #747C84; text-decoration: none; font-style: normal; }
.job-summary { padding-left: 20px; font-size: 0.9rem; }
.job-summary dt { color: #ADB3BA; font-weight: normal; }
.job-summary dd { color: #747C84; text-decoration: none; font-style: normal; }
.build-summary > div h3 { display: inline; line-height: 28px; font-size: 18px; }
.build-summary > div h3 a.material-icons { vertical-align: middle; text-decoration: none; color: #ADB3BA; font-size: 20px; padding-left: 5px; }
.job-list { padding-left: 20px; margin-bottom: 20px; }
.job-list a { text-decoration: none; color: #747C84; font-size: 15px; display: block; border-top: 1px solid #eee; padding-top: 20px; padding-bottom: 20px; position: relative; }
.job-list a h3 { margin-bottom: 10px; color: #2b303b; font-size: 14px; margin-top: 14px; }
.job-list a h3 .env { font-family: "Roboto Mono"; }
.job-list a:not(.active) > div:nth-child(2) > div { display: none; }
.job-list a:last-child { border-bottom: 1px solid #eee; }
.job-list a > div:first-child { margin-bottom: 10px; position: relative; }
.build-btn-group { margin-left: 20px; }
.build-btn-group .btn { background: #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-right: 10px; }
.build-btn-group .btn .btn-danger { border: 1px solid #bf616a; color: #bf616a; }
.build-btn-group .btn.btn-info { border-color: #95AEC7; color: #95AEC7; }
.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; border: none; outline: none; display: none; }
.tail i { color: rgba(255, 255, 255, 0.5); line-height: 38px; display: inline-block; }
@supports (position: sticky) { .sticky { position: sticky; top: 20px; } }
@supports not (position: sticky) { .sticky { top: 0px; } }
.user-row .col-sm-4 { width: 100%; }
.user-row .col-sm-4:last-child .card { border-bottom: none; }
.user-row .card { border: none; border-bottom: 1px solid #EEE; border-radius: 0px; position: relative; display: flex; }
.user-row .card-block h3 { font-size: 1.1rem; }
.user-row .card-block .card-text { color: rgba(0, 0, 0, 0.5); font-size: 0.95rem; margin: 0px; }
.user-row .card-block .btn-group { position: absolute; top: 15px; right: 0px; }
.user-row .card-block .btn-group .btn { background: #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; }
.user-row .card-block .btn-group .btn-danger { border: 1px solid #bf616a; color: #bf616a; }
.user-row .card-block .btn-group .btn-info { border-color: #95AEC7; color: #95AEC7; }
.user-row .card-header { background: #FFF; border-bottom: none; padding-right: 0px; padding-left: 0px; width: 45px; }
.card-header img { vertical-align: middle; width: 32px; height: 32px; border-radius: 3px; }
[data-admin="true"] h3:after { content: "admin"; text-transform: uppercase; color: #fff; display: inline-block; font-size: 12px; margin-left: 15px; background: #a3be8c; border-radius: 3px; padding: 2px 5px; }
.container.profile { max-width: 800px !important; margin-bottom: 40px; }
.container.profile .row { border-bottom: 1px solid #f0f4f7; }
.container.profile .row:last-child { border-bottom: none; }
.container.profile .col-md-3 { padding: 30px; padding-left: 0px; max-width: 200px; font-size: 15px; color: #2b303b; }
.container.profile .col-md-9 { padding: 30px; color: #65737e; font-size: 15px; }
.container.profile .btn.btn-info { border-color: #95AEC7; color: #95AEC7; background: #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; }
.container.profile pre { white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; font-family: "Roboto Mono"; color: #2b303b; }
.container.repo_config, .container.repo_secrets { max-width: 800px !important; }
.container.repo_config .row, .container.repo_secrets .row { border-bottom: 1px solid #f0f4f7; }
.container.repo_config .row:last-child, .container.repo_secrets .row:last-child { border: none; margin-bottom: 30px; }
.container.repo_config .row:last-child .col-md-12, .container.repo_secrets .row:last-child .col-md-12 { padding: 0px; }
.container.repo_config .row:nth-last-child(2), .container.repo_secrets .row:nth-last-child(2) { border: none; }
.container.repo_config .col-md-3, .container.repo_secrets .col-md-3 { padding: 30px; padding-left: 0px; max-width: 200px; font-size: 15px; color: #2b303b; }
.container.repo_config .col-md-9, .container.repo_secrets .col-md-9 { padding: 30px; color: #65737e; font-size: 15px; }
.container.repo_config .btn.btn-info, .container.repo_secrets .btn.btn-info { border-color: #95AEC7; color: #95AEC7; background: #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; }
.container.repo_config pre, .container.repo_secrets pre { white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; font-family: "Roboto Mono"; color: #2b303b; }
.container.repo_config input[type="range"] + span, .container.repo_secrets input[type="range"] + span { margin-left: 15px; }
.container.repo_config .alert.alert-danger, .container.repo_secrets .alert.alert-danger { background: #FFF; border: 1px solid #bf616a; border-radius: 2px; margin-top: 40px; color: #bf616a; }
.container.repo_config .alert.alert-danger .btn.btn-danger, .container.repo_secrets .alert.alert-danger .btn.btn-danger { margin-right: 15px; border: 1px solid #bf616a; color: #bf616a; background: #FFF; border-radius: 2px; font-size: 13px; text-transform: uppercase; }
.container.repo_secrets textarea { border: none; background: #eff1f5; width: 100%; border-radius: 3px; margin-bottom: 10px; height: 150px; padding: 10px 15px; }
.container.repo_secrets .btn.btn-info { margin-left: 0px; }
.container.repo_secrets .result { margin-top: 20px; white-space: pre; }
.container.repo_activate { max-width: 800px !important; }
.container.repo_activate .alert.alert-info { color: #3498DB; background: rgba(52, 152, 219, 0.12); border-radius: 2px; border: none; margin-top: 20px; padding: 30px; }
.container.repo_activate .btn.btn-info { color: #3498DB; background: #3498DB; border: 1px solid #3498DB; background: #FFF; border-radius: 2px; text-transform: uppercase; font-size: 13px; }
body.login { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; -webkit-box-align: center; -webkit-align-items: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: center; -webkit-justify-content: center; -ms-flex-pack: center; justify-content: center; width: 100%; height: 100%; overflow: hidden; }
body.login div { position: relative; width: 220px; height: 30px; }
body.login div div.logo { background-image: url(/static/images/logo_dark.svg); background-size: 35px; background-repeat: no-repeat; background-position: center center; position: absolute; top: 0px; left: 0px; height: 30px; width: 35px; animation-name: fadein; animation-duration: 1.5s; animation-timing-function: ease-in; }
body.login div input[type="submit"], body.login div a { background: #2b303b; color: #FFF; text-decoration: none; position: absolute; top: 0px; right: 0px; text-transform: uppercase; font-size: 13px; min-width: 150px; text-align: center; padding: 5px; border: none; animation-name: fadein; animation-duration: 1.5s; animation-timing-function: ease-in; }
body.login div.alert { position: fixed; top: 0px; left: 0px; right: 0px; line-height: 25px; padding: 20px; width: 100%; border: none; text-align: center; vertical-align: middle; height: auto; border-radius: 0px; }
body.login.login-form > div { width: 300px; height: 100px; display: flex; }
body.login.login-form > div div.logo { width: 65px; position: relative; background-position: left center; animation-name: none; }
body.login.login-form > div form { flex: 1 1 auto; }
body.login.login-form > div input[type="text"], body.login.login-form > div input[type="password"] { display: block; width: 100%; padding: 5px 10px; background-color: #eff1f5; border: none; color: #747C84; }
body.login.login-form > div input[type="password"] { margin-top: 1px; }
body.login.login-form > div input[type="submit"] { position: relative; width: 100%; margin-top: 20px; animation-name: none; }
@keyframes flyin { 0% { left: -3000px; }
100% { left: 0px; } }
@keyframes flyin_right { 0% { right: -3000px; }
100% { right: 0px; } }
@keyframes fadein { 0% { opacity: 0; }
100% { opacity: 1; } }
.repo-row .col-sm-4 { width: 100%; }
.repo-row .col-sm-4:last-child .card { border-bottom: none; }
.repo-row .card { border: none; border-bottom: 1px solid #EEE; border-radius: 0px; position: relative; display: flex; text-decoration: none; }
.repo-row .card-block h3 { font-size: 1.1rem; color: #2b303b; }
.repo-row .card-block .card-text { color: rgba(0, 0, 0, 0.5); font-size: 0.95rem; margin: 0px; }
.repo-row .card-block .btn-group { position: absolute; top: 15px; right: 0px; }
.repo-row .card-block .btn-group .btn-danger { color: #bf616a; background: #FFF; border: 1px solid #bf616a; 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; }
.repo-row .card-header { background: #FFF; border-bottom: none; padding-right: 0px; padding-left: 0px; width: 45px; }
.repo-search { color: #747C84; border: none; background-color: #eff1f5; border-radius: 0px; padding: 9px 15px; width: 100%; margin-bottom: 45px; border-radius: 2px; }
.toc { list-style-type: none; padding: 0px; margin: 0px; padding-bottom: 40px; }
.toc h2 { font-size: 21px; font-weight: normal; margin-bottom: 20px; color: #2b303b; }
.toc ul { list-style-type: none; padding: 0px; margin: 0px; margin-bottom: 40px; border-bottom: 1px solid #EEE; padding-bottom: 40px; }
.toc ul li a { color: #2b303b; text-decoration: none; }
.toc ul li a:hover { text-decoration: underline; }
.toc [data-method]:before { content: attr(data-method); padding: 0px 10px; line-height: 18px; min-width: 70px; font-size: 11px; text-transform: uppercase; display: inline-block; text-align: center; color: #FFF; border-radius: 2px; margin-right: 20px; }
.toc [data-method="GET"]:before { background-color: #1ABC9C; }
.toc [data-method="PUT"]:before { background-color: #9B59B6; }
.toc [data-method="POST"]:before { background-color: #3498DB; }
.toc [data-method="PATCH"]:before { background-color: #E67E22; }
.toc [data-method="DELETE"]:before { background-color: #E74C3C; }
.toc [data-method]:before { background: #FFF; border: 1px solid #FFF; }
.toc [data-method="GET"]:before { color: #1ABC9C; border-color: #1ABC9C; }
.toc [data-method="PUT"]:before { color: #9B59B6; border-color: #9B59B6; }
.toc [data-method="POST"]:before { color: #3498DB; border-color: #3498DB; }
.toc [data-method="PATCH"]:before { color: #E67E22; border-color: #E67E22; }
.toc [data-method="DELETE"]:before { color: #E74C3C; border-color: #E74C3C; }
.operation [data-method]:before { content: attr(data-method); padding: 0px 10px; line-height: 18px; min-width: 70px; font-size: 11px; text-transform: uppercase; display: inline-block; text-align: center; color: #FFF; border-radius: 2px; margin-right: 20px; background: #FFF; border: 1px solid #FFF; line-height: 20px; vertical-align: top; }
.operation [data-method]:before { background: #FFF; border: 1px solid #FFF; }
.operation [data-method="GET"]:before { color: #1ABC9C; border-color: #1ABC9C; }
.operation [data-method="PUT"]:before { color: #9B59B6; border-color: #9B59B6; }
.operation [data-method="POST"]:before { color: #3498DB; border-color: #3498DB; }
.operation [data-method="PATCH"]:before { color: #E67E22; border-color: #E67E22; }
.operation [data-method="DELETE"]:before { color: #E74C3C; border-color: #E74C3C; }
.docs { margin-top: 40px; padding: 0px 50px; padding-right: 40px; }
.docs-api pre { margin-right: 15px; font-size: 13px; color: #eff1f5; color: #2b303b; border-radius: 2px; background: #2b303b; background: #ECF0F1; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 25px 30px; font-family: "Roboto Mono"; margin-right: 0px; padding-left: 40px; background: #eff1f5; color: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 25px 30px; font-family: "Roboto Mono"; border-radius: 2px; font-size: 13px; }
.operation { min-height: 100vh; padding: 20px 0px; display: flex; }
.operation > aside, .operation > div { min-width: 50%; max-width: 50%; width: 0%; padding-right: 40px; }
.operation h2 { color: #2b303b; font-size: 21px; }
.operation h3 { font-size: 16px; margin-bottom: 20px; margin-top: 40px; }
.operation aside { background: rgba(43, 48, 59, 0.95); box-sizing: border-box; padding: 20px 0px 10px 0px; border-radius: 2px; }
.operation aside h4 { color: #d0d4d7; font-size: 15px; padding: 20px; padding-left: 40px; }
.operation aside pre { background: #2b303b; color: #d0d4d7; margin-right: 0px; padding-left: 40px; }
.operation .params { padding: 0px; margin: 0px; list-style-type: none; border-top: 1px solid #f0f4f7; }
.operation .params li { padding: 15px 10px; border-bottom: 1px solid #f0f4f7; font-size: 15px; }
.operation .params li p { line-height: 20px; margin: 0 0 0 170px; }
.operation .params li:after { visibility: hidden; display: block; font-size: 0; content: " "; clear: both; height: 0; }
.operation .params h4 { float: left; line-height: 20px; text-align: right; padding-right: 2 0px; width: 150px; font-weight: bold; font-size: 15px; }
.operation .params small { display: block; text-transform: uppercase; color: #E67E22; font-size: 11px; font-weight: normal; margin-top: 5px; }
.operation aside { background: rgba(239, 241, 245, 0.49); }
.operation aside h4 { color: #2b303b; }
.operation aside pre { background: #eff1f5; color: #2b303b; }
.docs-usage .row { display: flex; }
.docs-usage .row .content-nav { max-width: 250px; min-width: 250px; width: 250px; }
.docs-usage .row .content-nav ul { padding: 0px; margin: 0px; list-style-type: none; }
.docs-usage .row .content-nav ul li { position: relative; color: rgba(0, 0, 0, 0.5); text-transform: uppercase; font-size: 13px; }
.docs-usage .row .content-nav ul li a { color: rgba(43, 48, 59, 0.8); line-height: 30px; display: block; font-size: 16px; text-transform: none; }
.docs-usage .row .content-nav ul li.active a { color: #2b303b; font-size: 16px; text-transform: none; }
.docs-usage .row .content-nav ul li.active:before { content: ""; border-left: 4px solid #2b303b; position: absolute; top: 0px; bottom: 0px; left: -20px; }
.docs-usage .row .content-nav ul ul { margin-bottom: 25px; margin-top: 5px; }
.docs-usage .row .content-main { max-width: 900px; margin: 0px auto; margin-bottom: 40px; font-size: 14px; }
.docs-usage .row .content-main p { line-height: 20px; margin: 20px 0px; }
.docs-usage .row .content-main blockquote { color: #31708f; background-color: #d9edf7; border-color: #bcdff1; padding: 30px; margin: 15px 0px; border-radius: 2px; }
.docs-usage .row .content-main blockquote p:first-child { margin-top: 0px; }
.docs-usage .row .content-main blockquote p:last-child { margin-bottom: 0px; }
.docs-usage .row .content-main strong { font-weight: bold; }
.docs-usage .row .content-main h1 { margin: 40px 0px 20px 0px; font-size: 22px; }
.docs-usage .row .content-main h1:first-child { margin-top: 0px; }
.docs-usage .row .content-main h2 { margin: 40px 0px 30px 0px; font-size: 20px; border-top: 1px solid #EEE; padding-top: 30px; }
.docs-usage .row .content-main h3 { margin: 40px 0px 20px 0px; font-size: 16px; }
.docs-usage .row .content-main ul code, .docs-usage .row .content-main ol code, .docs-usage .row .content-main p code { background: #eff1f5; color: #2b303b; padding: 0px 7px; font-family: "Roboto Mono"; font-size: 13px; white-space: nowrap; }
.docs-usage .row .content-main pre { margin-right: 0px; padding-left: 40px; background: #eff1f5; color: #2b303b; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; padding: 25px 30px; font-family: "Roboto Mono"; border-radius: 2px; font-size: 13px; }
.docs-usage .row .content-main pre > code { background: #eff1f5; color: #2b303b; font-family: "Roboto Mono"; font-size: 13px; }
.tt-open { position: absolute; top: 34px; left: 0px; z-index: 100; display: none; background: #FFF; min-width: 100%; border: 1px solid #eee; border-radius: 0px; }
.tt-selectable:hover, .tt-cursor { background: #eff1f5; }
.tt-selectable { padding: 1rem 0.75rem; cursor: pointer; display: flex; }
.tt-selectable div:first-child { width: 75px; max-width: 75px; min-width: 75px; }
.tt-selectable div:first-child img { width: 32px; max-width: 32px; min-width: 32px; border-radius: 50%; text-align: left; margin-left: 1rem; }
.tt-selectable div:last-child { flex: 1 1 auto; line-height: 32px; }
.no-matches-message { padding: 20px; color: rgba(0, 0, 0, 0.5); font-style: italic; }
.not-indexed-message { padding: 20px; padding-bottom: 0px; color: rgba(0, 0, 0, 0.5); font-style: italic; }
.not-indexed-message em { color: #2b303b; }
.not-indexed-message p:last-child { margin-top: 20px; font-size: 15px; color: rgba(0, 0, 0, 0.4); }
.not-indexed-message p:last-child a { color: rgba(0, 0, 0, 0.8); }
.hidden { display: none; }
:focus { outline: none; }
::-moz-focus-inner { border: 0; }
.container { max-width: 980px !important; }
.modal-content { padding: 20px; }
.modal-content .form-control { border-radius: 2px; }
.modal-content .btn { margin-top: 20px; }
.node-row .col-sm-4 { width: 100%; }
.node-row .col-sm-4:last-child .card { border-bottom: none; }
.node-row .card { border: none; border-bottom: 1px solid #EEE; border-radius: 0px; position: relative; display: flex; }
.node-row .card-block h3 { font-size: 1.1rem; }
.node-row .card-block .card-text { color: rgba(0, 0, 0, 0.5); font-size: 0.95rem; margin: 0px; }
.node-row .card-block .btn-group { position: absolute; top: 15px; right: 0px; }
.node-row .card-block .btn-group .btn-danger { color: #bf616a; background: #FFF; border: 1px solid #bf616a; 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; }
.node-row .card-header { background: #FFF; border-bottom: none; padding-right: 0px; padding-left: 0px; width: 45px; }
i.linux_amd64 { width: 32px; height: 32px; display: inline-block; background: url(/static/images/ubuntu.svg); background-size: 32px; background-repeat: no-repeat; }

View file

@ -1,8 +0,0 @@
extends base
block append head
title Bad Request
block content
div.container
div.alert.alert-danger Bad Request

View file

@ -1,8 +0,0 @@
extends base
block append head
title Not Authorized
block content
div.container
div.alert.alert-danger Not Authorized

View file

@ -1,8 +0,0 @@
extends base
block append head
title Forbidden
block content
div.container
div.alert.alert-danger Access Forbidden.

View file

@ -1,8 +0,0 @@
extends base
block append head
title Not Found
block content
div.container
div.alert.alert-danger Not Found

View file

@ -1,8 +0,0 @@
extends base
block append head
title Internal Server Error
block content
div.container
div.alert.alert-danger Oops. Something went wrong.

View file

@ -1,71 +0,0 @@
extends base
block append head
title Dashboard
block menu
li
a[href="/"] Home
li
a[href="/settings/profile"] Profile
if User.Admin
li
a[href="/settings/people"] People
li
a[href="/settings/nodes"] Nodes
block content
h1 Feed
hr
table
tbody[data-bind="foreach: feed"]
tr
td
a[data-bind="text: full_name, attr: {href: full_name() + '/' + number() }"]
td[data-bind="text: $data.full_name"]
td[data-bind="text: $data.owner"]
td[data-bind="text: $data.name"]
td[data-bind="text: $data.private"]
td[data-bind="text: $data.trusted"]
td[data-bind="text: $data.avatar_url"]
td[data-bind="text: $data.clone_url"]
td[data-bind="text: $data.number"]
td[data-bind="text: $data.commit"]
td[data-bind="text: $data.branch"]
td[data-bind="text: $data.author"]
td[data-bind="text: $data.status"]
block append scripts
script
var feed = #{json(Feed)};
function Activity(data) {
this.full_name = ko.observable(data.full_name);
this.owner = ko.observable(data.owner);
this.name = ko.observable(data.name);
this.private = ko.observable(data.private);
this.trusted = ko.observable(data.trusted);
this.avatar_url = ko.observable(data.avatar);
this.clone_url = ko.observable(data.clone_url);
this.link_url = ko.observable(data.link_url);
this.number = ko.observable(data.number);
this.commit = ko.observable(data.commit);
this.started_at = ko.observable(data.started_at);
this.finished_at = ko.observable(data.finished_at);
this.exit_code = ko.observable(data.exit_code);
this.status = ko.observable(data.status);
this.environment = ko.observable(data.environment);
}
function FeedViewModel() {
var self = this;
var mapped = $.map(feed, function(activity) {
return new Activity(activity)
});
self.feed = ko.observableArray(mapped);
}
ko.applyBindings(new FeedViewModel());

View file

@ -1,97 +0,0 @@
extends base
block append head
title Users
block content
div.container
h1 Users
hr
form
input[type="text"][placeholder="ie octocat"][data-bind="value: newUserLogin"]
button[type="button"][data-bind="click: addUser"] add user
hr
if len(Users) <= 1
div.alert.alert-info
| You should add users
table
tbody[data-bind="foreachInit: users"]
tr[data-template]
td[data-bind="text: $data.login"]
td[data-bind="text: $data.email"]
td[data-bind="text: $data.avatar_url"]
td[data-bind="text: $data.active"]
td[data-bind="text: $data.admin"]
td
button.delete[data-bind="click: $parent.removeUser"] delete
td
button.delete[data-bind="click: $parent.toggleAdmin"] toggle
each $user in Users
tr[data-init]
td[data-bind="init, text: login"] #{$user.Login}
td[data-bind="init, text: email"] #{$user.Email}
td[data-bind="init, text: avatar_url"] #{$user.Avatar}
td[data-bind="init, text: active"] #{$user.Active}
td[data-bind="init, text: admin"] #{$user.Admin}
td
button.delete[data-bind="init, click: $parent.removeUser"] delete
td
button.delete[data-bind="init, click: $parent.toggleAdmin"] toggle
block append scripts
script
function User(data) {
this.login = ko.observable(data.login);
this.email = ko.observable(data.email);
this.avatar_url = ko.observable(data.avatar_url);
this.active = ko.observable(data.active);
this.admin = ko.observable(data.admin);
}
function UserViewModel() {
var self = this;
self.users = ko.observableArray();
self.newUserLogin = ko.observable();
self.removeUser = function(user) {
$.ajax({
url: "/api/users/"+user.login(),
type: "DELETE",
success: function( data ) {
self.users.remove(user);
self.users.sort(self.sort);
}
});
};
self.addUser = function() {
$.ajax({
url: "/api/users",
type: "POST",
data: ko.toJSON({ "login": self.newUserLogin() }),
contentType: "application/json",
success: function( data ) {
self.newUserLogin("");
self.users.push(new User(data));
self.users.sort(self.sort);
}
});
};
self.toggleAdmin = function(user) {
user.admin(!user.admin());
$.ajax({
url: "/api/users/"+user.login(),
type: "PATCH",
data: ko.toJSON(user),
contentType: "application/json"
});
};
self.sort = function(a, b) {
return a.login().toLowerCase() > b.login().toLowerCase() ? 1 : -1;
};
}
ko.applyBindings(new UserViewModel());

View file

@ -1,54 +0,0 @@
doctype 5
html
head
block head
meta[charset="utf-8"]
meta[name="viewport"][content="width=device-width, initial-scale=1"]
meta[http-equiv="x-ua-compatible"][content="ie=edge"]
link[rel="icon"][type="image/x-icon"][href="/static/images/favicon.ico"]
link[rel="stylesheet"][href="//fonts.googleapis.com/icon?family=Material+Icons"]
link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto+Mono"]
link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto"]
link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"]
link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/octicons/3.1.0/octicons.min.css"]
link[rel="stylesheet"][href="/static/styles_gen/style.css"]
if Csrf
meta[name="_csrf"][content=Csrf]
body
nav.navbar
div.container-fluid
a.navbar-brand[href="/"]
if User
ul.nav.navbar-nav.navbar-right
li
form.navbar-form
input.form-control.typeahead[type="text"][placeholder="Search..."]
li
img[src=User.Avatar]
li
div.dropdown
button[type="button"][data-toggle="dropdown"]
i.material-icons expand_more
div.dropdown-menu.dropdown-menu-right
a.dropdown-item[href="/settings/profile"] Profile
a.dropdown-item[href="/logout"] Logout
div.subnav
div.container-fluid
block header
div#content
block content
block scripts
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"]
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js"]
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery-searcher/0.2.0/jquery.searcher.min.js"]
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"]
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"]
script[text="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/stickyfill/1.1.2/stickyfill.js"]
script[type="text/javascript"][src="/static/scripts_gen/drone.min.js"]

View file

@ -1,99 +0,0 @@
extends base
block append head
title #{Repo.Name} · #{Build.Number}
block header
ol
li
a[href="/"+Repo.FullName]
| #{Repo.Owner} / #{Repo.Name}
li.separator
i.material-icons chevron_right
li #{Build.Number}
block content
div.container-fluid
div.row
div.col-md-4.sticky
div.build-summary
div
div[class=Build.Status][style="display:none"] #{Build.Status}
h3
| #{Build.Message}
a.material-icons[href=Build.Link][target="_blank"] link
p
em #{Build.Author}
if Build.Event != "deployment"
span authored
else
span deployed
em[data-livestamp=Build.Created]
span to
if Build.Event != "deployment"
em #{Build.Branch}
else
em #{Build.Deploy}
div.job-list
$curr = Job
$build = Build
$repo = Repo
each $job in Jobs
a[href="/" + $repo.FullName +"/"+ $build.Number+"/"+$job.Number][data-job=$job.Number]
.active ? $curr.Number == $job.Number
div
div.status[class=$job.Status] #{$job.Status}
div
if len($job.Environment) != 0
h3
each $key, $val in $job.Environment
div.env #{$key}=#{$val}
div[class="msg-pending"]
.hidden ? $job.Status != "pending"
| pending assignment to a worker
div[class="msg-running"]
.hidden ? $job.Status != "running"
| started
span[data-livestamp=$job.Started]
div[class="msg-finished"]
.hidden ? $job.Finished == 0
| finished
span[data-livestamp=$job.Finished]
div[class="msg-exited"]
.hidden ? $job.Finished == 0
| with exit code
span #{$job.ExitCode}
div.build-btn-group
button.btn.btn-info.hidden#restart restart
button.btn.btn-info.hidden#cancel cancel
div.col-md-8
if Build.Signed
if Build.Verified
noscript
else
div.alert.alert-warning
| Your .drone.yml.sig file did not match your .drone.yml
if Job.Error != ""
div.alert.alert-danger #{Job.Error}
else
div#output
button.tail#tail
i.material-icons expand_more
block append scripts
script
$('.sticky').Stickyfill();
var repo = #{json(Repo.FullName)};
var build = #{json(Build.Number)};
var job = #{json(Job.Number)};
var status = #{json(Job.Status)};
var view = new JobViewModel(repo, build, job, status);

View file

@ -1,40 +0,0 @@
extends base
block append head
title #{Site.Name} · Drone
block header
ol
li Documentation
ul.nav.nav-tabs
li.nav-item
a[class="nav-link"][href="../setup/"]
.active ? Site.Name == "Install"
| Install
li.nav-item
a[class="nav-link"][href="../build/"]
.active ? Site.Name == "Builds"
| Builds
li.nav-item
a[class="nav-link"][href="../plugin/"]
.active ? Site.Name == "Plugins"
| Plugins
li.nav-item
a[class="nav-link"][href="../cli/"]
.active ? Site.Name == "CLI"
| CLI
li.nav-item
a.nav-link[href="../api/"] API Reference
block content
div.container-fluid.docs.docs-usage
div.row
div.content-nav
ul
#{Site.Nav.HTML}
div.content-main
#{Page.HTML}
block scripts
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"]
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"]

View file

@ -1,46 +0,0 @@
extends base
block append head
title Active Repositories
block header
ol
li Active Repositories
ul.nav.nav-tabs
li.nav-item
a.nav-link.active[href="/"] Active Repositories
li.nav-item
a.nav-link[href="/repos"] Available Repositories
block content
div.container
if len(Repos) == 0
div.alert.alert-info
| Your repository list is empty.
else
div.row.repo-row
input.repo-search[type="search"][placeholder="Filter..."]
div.repo-list
each $repo in Repos
div.col-sm-4
a.card[href="/"+$repo.FullName]
div.card-header
if $repo.Avatar != ""
img.avatar[src=$repo.Avatar]
else
img.avatar[src="/static/images/dummy.png"]
div.card-block
h3.login #{$repo.Name}
div.full_name.hidden #{$repo.FullName}
block append scripts
if len(Repos) != 0
script
$(window).load(function(){
$(".repo-list").searcher({
itemSelector: ".col-sm-4",
textSelector: ".full_name",
inputSelector: ".repo-search"
});
});

View file

@ -1,35 +0,0 @@
doctype 5
html
head
title Login
meta[charset="utf-8"]
meta[name="viewport"][content="width=device-width, initial-scale=1"]
meta[http-equiv="x-ua-compatible"][content="ie=edge"]
link[rel="icon"][type="image/x-icon"][href="/static/images/favicon.ico"]
link[rel="stylesheet"][href="//fonts.googleapis.com/icon?family=Material+Icons"]
link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto+Mono"]
link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto"]
link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"]
link[rel="stylesheet"][href="/static/styles_gen/style.css"]
style
html { height: 100%; overflow: hidden; }
body.login
div
div.logo
a[href="/authorize"] Login
if Error == "oauth_error"
div.alert.alert-danger
| We failed to authorize your account. Please contact your
| system administrator to check the logs and see what went wrong.
else if Error == "access_denied"
div.alert.alert-danger
| Unable to login. Registration is closed.
else if Error == "internal_error"
div.alert.alert-danger
| We encountered an unexpected error. Please contact your
| system administrator to check the logs and see what went wrong.

View file

@ -1,24 +0,0 @@
doctype 5
html
head
title Login
meta[charset="utf-8"]
meta[name="viewport"][content="width=device-width, initial-scale=1"]
meta[http-equiv="x-ua-compatible"][content="ie=edge"]
link[rel="icon"][type="image/x-icon"][href="/static/images/favicon.ico"]
link[rel="stylesheet"][href="//fonts.googleapis.com/icon?family=Material+Icons"]
link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto+Mono"]
link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Roboto"]
link[rel="stylesheet"][href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.min.css"]
link[rel="stylesheet"][href="/static/styles_gen/style.css"]
style
html { height: 100%; overflow: hidden; }
body.login.login-form
div
div.logo
form[action="/authorize"][method="post"]
input[type="text"][placeholder="Username"][name="username"]
input[type="password"][placeholder="Password"][name="password"]
input[type="submit"][value="Login"]

View file

@ -1,52 +0,0 @@
extends base
block append head
title Docker Nodes
block header
ol
li Docker Nodes
li
button.btn.btn-info[data-toggle="modal"][data-target=".modal-node"]
| add node
block content
div.container
div.alert.alert-danger.hidden
div.row.node-row
each $node in Nodes
div.col-sm-4
div.card[data-id=$node.ID]
div.card-header
i[class="linux_amd64"]
div.card-block
h3.addr #{$node.Addr}
p.arch.card-text #{$node.Arch}
div.btn-group
button.btn.btn-danger Delete
div.modal.modal-node[role="dialog"]
div.modal-dialog
div.modal-content
form
fieldset.form-group
label[for="addr"] Address
input.form-control[type="text"][placeholder="unix:///var/run/docker.sock"]#addr
fieldset.form-group
label[for="key"] Key
textarea.form-control#key
fieldset.form-group
label[for="cert"] Cert
textarea.form-control#cert
fieldset.form-group
label[for="ca"] CA
textarea.form-control#ca
button.btn.btn-info[type="button"] add node
block append scripts
script
var view = new NodeViewModel();

View file

@ -1,59 +0,0 @@
extends base
block append head
title #{Repo.Name}
block header
ol
li #{Repo.Owner} / #{Repo.Name}
ul.nav.nav-tabs
li.nav-item
a.nav-link.active[href="/"+Repo.FullName] Builds
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings/badges"] Badges
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings/encrypt"] Secrets
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings"] Settings
block content
div.container
if len(Builds) == 0
div.alert.alert-info
| You have no builds
div.timeline
$repo = Repo
each $group in Groups
div.group
div.group-title
| commits on #{$group.Date}
each $build in $group.Builds
a.card[href=$repo.Name+"/"+$build.Number][data-build=$build.Number]
div.card-header
if $build.Avatar != ""
img[src=$build.Avatar]
else
img[src="/static/images/dummy.png"]
div.card-block
div
div.status[class=$build.Status] #{$build.Status}
h3 #{$build.Message}
p.card-text
em #{$build.Author}
if $build.Event != "deployment"
span authored
else
span deployed
em[data-livestamp=$build.Created]
span to
if $build.Event != "deployment"
em #{$build.Branch}
else
em #{$build.Deploy}
block append scripts
script
new RepoViewModel("#{Repo.FullName}");

View file

@ -1,33 +0,0 @@
extends base
block append head
title #{Repo.Name}
block header
ol
li #{Repo.Owner} / #{Repo.Name}
block content
div.container.repo_activate
div.alert.alert-danger.hidden
div.alert.alert-info
| This repository is not yet activated.
button.btn.btn-info#activateRepo Activate Now
block append scripts
script
$( "#activateRepo" ).click(function() {
$( "#activateRepo" ).hide();
$.ajax({
type: "POST",
url: "/api/repos/#{Repo.FullName}",
success: function() {
window.location.href="/#{Repo.FullName}"
},
error: function(err) {
$(".alert-info").hide();
$(".alert-danger").text("Unable to activate this repository. You must have administrative access to this repository. Also, please check the third-party application access policy to ensure Drone is approved. "+ err.responseText).show();
}
});
});

View file

@ -1,37 +0,0 @@
extends base
block append head
title #{Repo.Name}
block header
ol
li #{Repo.Owner} / #{Repo.Name}
ul.nav.nav-tabs
li.nav-item
a.nav-link[href="/"+Repo.FullName] Builds
li.nav-item
a.nav-link.active[href="/"+Repo.FullName+"/settings/badges"] Badges
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings/encrypt"] Secrets
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings"] Settings
block content
div.container.repo_config
div.row
div.col-md-3 Markdown
div.col-md-9
pre
| [![Build Status](#{Link}/api/badges/#{Repo.FullName}/status.svg)](#{Link}/#{Repo.FullName})
div.row
div.col-md-3 Markup
div.col-md-9
pre
| &lt;a href=&quot;#{Link}/#{Repo.FullName}&quot;&gt;&lt;img src=&quot;#{Link}/api/badges/#{Repo.FullName}/status.svg&quot; /&gt;&lt;/a&gt;
div.row
div.col-md-3 CC Menu
div.col-md-9
pre
| #{Link}/api/badges/#{Repo.FullName}/cc.xml

View file

@ -1,77 +0,0 @@
extends base
block append head
title #{Repo.Name}
block header
ol
li #{Repo.Owner} / #{Repo.Name}
ul.nav.nav-tabs
li.nav-item
a.nav-link[href="/"+Repo.FullName] Builds
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings/badges"] Badges
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings/encrypt"] Secrets
li.nav-item
a.nav-link.active[href="/"+Repo.FullName+"/settings"] Settings
block content
div.container.repo_config
div.row
div.col-md-3 Push Hooks
div.col-md-9
if Repo.AllowPush
input#push[type="checkbox"][hidden="hidden"][checked]
else
input#push[type="checkbox"][hidden="hidden"]
label.switch[for="push"]
div.row
div.col-md-3 Pull Request Hooks
div.col-md-9
if Repo.AllowPull
input#pull[type="checkbox"][hidden="hidden"][checked]
else
input#pull[type="checkbox"][hidden="hidden"]
label.switch[for="pull"]
div.row
div.col-md-3 Tag Hooks
div.col-md-9
if Repo.AllowTag
input#tag[type="checkbox"][hidden="hidden"][checked]
else
input#tag[type="checkbox"][hidden="hidden"]
label.switch[for="tag"]
div.row
div.col-md-3 Deploy Hook
div.col-md-9
if Repo.AllowDeploy
input#deploy[type="checkbox"][hidden="hidden"][checked]
else
input#deploy[type="checkbox"][hidden="hidden"]
label.switch[for="deploy"]
div.row
div.col-md-3 Timeout in Minutes
div.col-md-9
input[type="range"][min="0"][max="900"][value=Repo.Timeout]
span.timeout-label
| #{Repo.Timeout} minutes
div.row
div.col-md-3 Trusted
div.col-md-9
if Repo.IsTrusted
input#trusted[type="checkbox"][hidden="hidden"][checked]
else
input#trusted[type="checkbox"][hidden="hidden"]
label.switch[for="trusted"]
div.row
div.col-md-12
div.alert.alert-danger
button.btn.btn-danger Delete
span Permanently deletes the build history. This action cannot be undone.
block append scripts
script
var view = new RepoConfigViewModel(#{Repo.FullName});

View file

@ -1,45 +0,0 @@
extends base
block append head
title #{Repo.Name}
block header
ol
li #{Repo.Owner} / #{Repo.Name}
ul.nav.nav-tabs
li.nav-item
a.nav-link[href="/"+Repo.FullName] Builds
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings/badges"] Badges
li.nav-item
a.nav-link.active[href="/"+Repo.FullName+"/settings/encrypt"] Secrets
li.nav-item
a.nav-link[href="/"+Repo.FullName+"/settings"] Settings
block content
div.container.repo_secrets
div
textarea.secrets[spellcheck="false"]
div.alert.alert-danger.hidden
button.btn.btn-info#encryptButton Generate
div
pre.result
block append scripts
script
$("#encryptButton").click(function(){
$( ".alert-danger" ).hide();
$.ajax({
url: "/api/repos/#{Repo.FullName}/encrypt",
type: "POST",
contentType: "text/plain",
data: $(".secrets").val(),
success: function( data ) {
$( ".result" ).text( data );
},
error: function( data ) {
$( ".alert-danger" ).text(data.responseText).show();
}
});
});

View file

@ -1,47 +0,0 @@
extends base
block append head
title Available Repositories
block header
ol
li Available Repositories
ul.nav.nav-tabs
li.nav-item
a.nav-link[href="/"] Active Repositories
li.nav-item
a.nav-link.active[href="/repos"] Available Repositories
block content
div.container
if len(Repos) == 0
div.alert.alert-info
| Your repository list is empty.
else
div.row.repo-row
input.repo-search[type="search"][placeholder="Filter..."]
div.repo-list
each $repo in Repos
div.col-sm-4
a.card[href="/"+$repo.FullName]
div.card-header
if $repo.Avatar != ""
img.avatar[src=$repo.Avatar]
else
img.avatar[src="/static/images/dummy.png"]
div.card-block
h3.login #{$repo.Name}
div.full_name.hidden #{$repo.FullName}
block append scripts
if len(Repos) != 0
script
$(window).load(function(){
$(".repo-list").searcher({
itemSelector: ".col-sm-4",
textSelector: ".full_name",
inputSelector: ".repo-search"
});
});

View file

@ -1,76 +0,0 @@
extends base
block append head
title API · Drone
block header
ol
li Documentation
ul.nav.nav-tabs
li.nav-item
a.nav-link[href="../setup/"] Install
li.nav-item
a.nav-link[href="../build/"] Builds
li.nav-item
a.nav-link[href="../plugin/"] Plugins
li.nav-item
a.nav-link[href="../cli/"] CLI
li.nav-item
a.nav-link.active[href="#"] API Reference
block content
div.container-fluid.docs.docs-api
a[name="top"]
div.row
ul.toc
each $tag in Swagger.Tags
li
h2 #{$tag.Name}
ul
each $op in $tag.Ops
li
a[href="#"+$op.ID][data-method=$op.Method] #{$op.Summary}
div.row
each $tag in Swagger.Tags
each $op in $tag.Ops
a[name=$op.ID]
div.operation
div
h2[data-method=$op.Method] #{$op.Summary}
p #{$op.Desc}
h3 Request Parameters
ul.params
each $param in $op.Params
li
h4
| #{$param.Name}
small Required
p #{$param.Desc}
h3 Response Messages
ul.params
each $result in $op.Results
li
h4
| #{$result.Status}
p #{$result.Desc}
aside
h4 Endpoint
pre #{$op.Method} /api#{$op.Path}
each $param in $op.Params
if $param.Example
h4 Example Request
if $param.IsObject
pre #{$param.Example}
each $res in $op.Results
if $res.Example
h4 Example Response
if $res.IsArray
pre [#{$res.Example}]
else if $res.IsObject
pre #{$res.Example}
block scripts
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"]
script[type="text/javascript"][src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"]

View file

@ -1,33 +0,0 @@
extends base
block append head
title Profile
block header
ol
li Account Profile
block content
div.container.profile
div.row
div.col-md-3 Login
div.col-md-9 #{User.Login}
div.row
div.col-md-3 Email
div.col-md-9 #{User.Email}
div.row
div.col-md-3 Token
div.col-md-9
button.btn.btn-info#showToken show token
pre.result
block append scripts
script
$( "#showToken" ).click(function() {
$( "#showToken" ).hide();
$.post( "/api/user/token", function( data ) {
$( ".result" ).text( data );
});
});

View file

@ -1,40 +0,0 @@
extends base
block append head
title Users
block header
ol
li User Management
li
button.btn.btn-info[data-toggle="modal"][data-target=".modal-user"]
| add user
block content
div.container
div.alert.alert-danger.hidden
div.row.user-row
each $user in Users
div.col-sm-4
div.card[data-id=$user.Login][data-admin=$user.Admin]
div.card-header
img.avatar[src=$user.Avatar]
div.card-block
h3.login #{$user.Login}
p.email.card-text #{$user.Email}
div.btn-group
button.btn.btn-info Toggle Admin
button.btn.btn-danger Delete
div.modal.modal-user[role="dialog"]
div.modal-dialog
div.modal-content
form
label[for="login"] Username
input.form-control[type="text"][placeholder="ie octocat"]#login
button.btn.btn-info[type="button"] add user
block append scripts
script
var view = new UserViewModel();

View file

@ -1,59 +0,0 @@
package template
//go:generate sh -c "amberc amber/400.amber > amber_gen/400.html"
//go:generate sh -c "amberc amber/401.amber > amber_gen/401.html"
//go:generate sh -c "amberc amber/403.amber > amber_gen/403.html"
//go:generate sh -c "amberc amber/404.amber > amber_gen/404.html"
//go:generate sh -c "amberc amber/500.amber > amber_gen/500.html"
//go:generate sh -c "amberc amber/build.amber > amber_gen/build.html"
//go:generate sh -c "amberc amber/login.amber > amber_gen/login.html"
//go:generate sh -c "amberc amber/login_form.amber > amber_gen/login_form.html"
//go:generate sh -c "amberc amber/repos.amber > amber_gen/repos.html"
//go:generate sh -c "amberc amber/repo.amber > amber_gen/repo.html"
//go:generate sh -c "amberc amber/repo_badge.amber > amber_gen/repo_badge.html"
//go:generate sh -c "amberc amber/repo_activate.amber > amber_gen/repo_activate.html"
//go:generate sh -c "amberc amber/repo_config.amber > amber_gen/repo_config.html"
//go:generate sh -c "amberc amber/repo_secret.amber > amber_gen/repo_secret.html"
//go:generate sh -c "amberc amber/users.amber > amber_gen/users.html"
//go:generate sh -c "amberc amber/user.amber > amber_gen/user.html"
//go:generate sh -c "amberc amber/nodes.amber > amber_gen/nodes.html"
//go:generate sh -c "amberc amber/index.amber > amber_gen/index.html"
//go:generate go-bindata -pkg template -o template_gen.go amber_gen/
import (
"encoding/json"
"html/template"
"path/filepath"
"github.com/eknkc/amber"
)
func Load() *template.Template {
amber.FuncMap["json"] = marshal
dir, _ := AssetDir("amber_gen")
tmpl := template.New("_")
tmpl.Funcs(amber.FuncMap)
for _, name := range dir {
if filepath.Ext(name) != ".html" {
continue
}
path := filepath.Join("amber_gen", name)
src := MustAsset(path)
tmpl = template.Must(
tmpl.New(name).Parse(string(src)),
)
}
return tmpl
}
// marshal is a helper function to render data as JSON
// inside the template.
func marshal(v interface{}) template.JS {
a, _ := json.Marshal(v)
return template.JS(a)
}