From 83577a7d5de9880d5fccaea1a642d67581283e0d Mon Sep 17 00:00:00 2001 From: Brad Rydzewski Date: Sat, 21 Jun 2014 14:22:38 -0700 Subject: [PATCH] removed css files. designers will be providing removed amber files. replacing with angular removed queue package in favor or worker package removed channel package in favor of pubsub package --- .gitignore | 3 +- Makefile | 55 +-- server/amber.go | 287 ------------- server/{static/images => app}/favicon.ico | Bin server/{static/images => app}/favicon.png | Bin server/app/index.html | 39 ++ server/app/robots.txt | 3 + server/app/scripts/app.js | 395 ++++++++++++++++++ server/app/scripts/commit_updates.js | 98 +++++ server/app/scripts/controllers/build.js | 1 + server/app/scripts/controllers/conf.js | 1 + server/app/scripts/controllers/home.js | 14 + server/app/scripts/controllers/repo.js | 1 + server/app/scripts/controllers/setup.js | 36 ++ server/app/scripts/controllers/user.js | 38 ++ server/app/scripts/controllers/users.js | 14 + server/app/scripts/filters/filters.js | 62 +++ .../{static => app}/scripts/line_formatter.js | 0 server/{static => app}/scripts/main.js | 0 server/app/scripts/services/auth.js | 34 ++ server/app/scripts/services/conf.js | 14 + server/app/scripts/services/repo.js | 14 + server/app/scripts/services/user.js | 31 ++ .../scripts/util}/commit_updates.js | 0 server/app/scripts/util/line_formatter.js | 66 +++ .../{static => app}/styles/base/clearfix.less | 0 .../{static => app}/styles/base/overflow.less | 0 .../styles/base/variables.less | 0 server/{static => app}/styles/drone.css | 0 server/{static => app}/styles/drone.css.bak | 0 server/{static => app}/styles/drone.less | 0 server/{static => app}/styles/drone.min.css | 0 .../styles/general/angular.less | 0 .../styles/general/buttons.less | 0 .../{static => app}/styles/general/forms.less | 0 .../styles/general/resets.less | 0 .../{static => app}/styles/layout/layout.less | 0 .../{static => app}/styles/modules/alert.less | 0 .../styles/modules/article.less | 0 .../styles/modules/authors.less | 0 .../styles/modules/branches.less | 0 .../{static => app}/styles/modules/build.less | 0 .../{static => app}/styles/modules/card.less | 0 .../styles/modules/charts.less | 0 .../styles/modules/form-search.less | 0 .../styles/modules/header.less | 0 .../styles/modules/list-activity.less | 0 .../styles/modules/list-commits.less | 0 .../styles/modules/list-repos.less | 0 .../styles/modules/list-users.less | 0 .../{static => app}/styles/modules/nav.less | 0 .../styles/modules/section.less | 0 .../styles/modules/stdout.less | 0 .../styles/modules/switch.less | 0 .../styles/modules/user-account.less | 0 .../styles/vendor/font-awesome.less | 0 .../styles/vendor/pure-base.less | 0 .../styles/vendor/pure-forms.less | 0 .../styles/vendor/pure-grids.less | 0 .../styles/vendor/pure-menus.less | 0 server/app/views/account.html | 64 +++ server/app/views/branch.html | 51 +++ server/app/views/commit.html | 18 + server/app/views/header.html | 12 + server/app/views/home.html | 1 + server/app/views/index.html | 53 +++ server/app/views/login.html | 19 + server/app/views/repo.html | 71 ++++ server/app/views/repo_conf.html | 71 ++++ server/app/views/setup.html | 57 +++ server/app/views/sys_config.html | 115 +++++ server/app/views/sys_users.html | 34 ++ server/channel/channel.go | 157 ------- server/channel/conn.go | 36 -- server/channel/hub.go | 133 ------ server/database/remote.go | 89 ++++ server/database/remote_test.go | 196 +++++++++ server/database/schema/schema.go | 29 ++ server/database/server.go | 111 +++++ server/database/server_test.go | 201 +++++++++ server/database/testdata/testdata.go | 8 + server/database/user.go | 15 + server/handler/commit.go | 14 +- server/handler/hook.go | 17 +- server/handler/login.go | 5 +- server/handler/remote.go | 108 +++++ server/handler/repo.go | 2 +- server/handler/server.go | 88 ++++ server/handler/site.go | 186 --------- server/handler/ws.go | 216 ++++++++++ server/main.go | 93 +++-- server/pubsub/buffer.go | 30 ++ server/pubsub/channel.go | 118 ++++++ server/pubsub/opts.go | 21 + server/pubsub/pubsub.go | 75 ++++ server/pubsub/subscribe.go | 28 ++ server/queue/build_runner.go | 41 -- server/queue/queue.go | 47 --- server/queue/worker.go | 260 ------------ server/template/400.amber | 4 - server/template/401.amber | 4 - server/template/403.amber | 4 - server/template/404.amber | 4 - server/template/admin_conf.amber | 6 - server/template/admin_users.amber | 6 - server/template/base.amber | 33 -- server/template/html/400.html | 32 -- server/template/html/401.html | 32 -- server/template/html/403.html | 32 -- server/template/html/404.html | 32 -- server/template/html/admin_conf.html | 32 -- server/template/html/admin_users.html | 32 -- server/template/html/base.html | 31 -- server/template/html/index.html | 110 ----- server/template/html/login.html | 55 --- server/template/html/repo_branch.html | 83 ---- server/template/html/repo_commit.html | 104 ----- server/template/html/repo_conf.html | 126 ------ server/template/html/repo_feed.html | 111 ----- server/template/html/user_conf.html | 59 --- server/template/html/user_feed.html | 69 --- server/template/html/user_login.html | 32 -- server/template/html/user_repos.html | 71 ---- server/template/login.amber | 21 - server/template/repo_branch.amber | 54 --- server/template/repo_commit.amber | 71 ---- server/template/repo_conf.amber | 80 ---- server/template/repo_feed.amber | 84 ---- server/template/user_conf.amber | 26 -- server/template/user_feed.amber | 39 -- server/template/user_login.amber | 4 - server/template/user_repos.amber | 40 -- server/worker/dispatch.go | 46 ++ server/worker/request.go | 12 + server/worker/worker.go | 160 +++++++ shared/build/docker/client.go | 17 + shared/model/remote.go | 20 + shared/model/server.go | 19 + 138 files changed, 3133 insertions(+), 2760 deletions(-) delete mode 100644 server/amber.go rename server/{static/images => app}/favicon.ico (100%) rename server/{static/images => app}/favicon.png (100%) create mode 100644 server/app/index.html create mode 100644 server/app/robots.txt create mode 100644 server/app/scripts/app.js create mode 100644 server/app/scripts/commit_updates.js create mode 100644 server/app/scripts/controllers/build.js create mode 100644 server/app/scripts/controllers/conf.js create mode 100644 server/app/scripts/controllers/home.js create mode 100644 server/app/scripts/controllers/repo.js create mode 100644 server/app/scripts/controllers/setup.js create mode 100644 server/app/scripts/controllers/user.js create mode 100644 server/app/scripts/controllers/users.js create mode 100644 server/app/scripts/filters/filters.js rename server/{static => app}/scripts/line_formatter.js (100%) rename server/{static => app}/scripts/main.js (100%) create mode 100644 server/app/scripts/services/auth.js create mode 100644 server/app/scripts/services/conf.js create mode 100644 server/app/scripts/services/repo.js create mode 100644 server/app/scripts/services/user.js rename server/{static/scripts => app/scripts/util}/commit_updates.js (100%) create mode 100644 server/app/scripts/util/line_formatter.js rename server/{static => app}/styles/base/clearfix.less (100%) rename server/{static => app}/styles/base/overflow.less (100%) rename server/{static => app}/styles/base/variables.less (100%) rename server/{static => app}/styles/drone.css (100%) rename server/{static => app}/styles/drone.css.bak (100%) rename server/{static => app}/styles/drone.less (100%) rename server/{static => app}/styles/drone.min.css (100%) rename server/{static => app}/styles/general/angular.less (100%) rename server/{static => app}/styles/general/buttons.less (100%) rename server/{static => app}/styles/general/forms.less (100%) rename server/{static => app}/styles/general/resets.less (100%) rename server/{static => app}/styles/layout/layout.less (100%) rename server/{static => app}/styles/modules/alert.less (100%) rename server/{static => app}/styles/modules/article.less (100%) rename server/{static => app}/styles/modules/authors.less (100%) rename server/{static => app}/styles/modules/branches.less (100%) rename server/{static => app}/styles/modules/build.less (100%) rename server/{static => app}/styles/modules/card.less (100%) rename server/{static => app}/styles/modules/charts.less (100%) rename server/{static => app}/styles/modules/form-search.less (100%) rename server/{static => app}/styles/modules/header.less (100%) rename server/{static => app}/styles/modules/list-activity.less (100%) rename server/{static => app}/styles/modules/list-commits.less (100%) rename server/{static => app}/styles/modules/list-repos.less (100%) rename server/{static => app}/styles/modules/list-users.less (100%) rename server/{static => app}/styles/modules/nav.less (100%) rename server/{static => app}/styles/modules/section.less (100%) rename server/{static => app}/styles/modules/stdout.less (100%) rename server/{static => app}/styles/modules/switch.less (100%) rename server/{static => app}/styles/modules/user-account.less (100%) rename server/{static => app}/styles/vendor/font-awesome.less (100%) rename server/{static => app}/styles/vendor/pure-base.less (100%) rename server/{static => app}/styles/vendor/pure-forms.less (100%) rename server/{static => app}/styles/vendor/pure-grids.less (100%) rename server/{static => app}/styles/vendor/pure-menus.less (100%) create mode 100644 server/app/views/account.html create mode 100644 server/app/views/branch.html create mode 100644 server/app/views/commit.html create mode 100644 server/app/views/header.html create mode 100644 server/app/views/home.html create mode 100644 server/app/views/index.html create mode 100644 server/app/views/login.html create mode 100644 server/app/views/repo.html create mode 100644 server/app/views/repo_conf.html create mode 100644 server/app/views/setup.html create mode 100644 server/app/views/sys_config.html create mode 100644 server/app/views/sys_users.html delete mode 100644 server/channel/channel.go delete mode 100644 server/channel/conn.go delete mode 100644 server/channel/hub.go create mode 100644 server/database/remote.go create mode 100644 server/database/remote_test.go create mode 100644 server/database/server.go create mode 100644 server/database/server_test.go create mode 100644 server/handler/remote.go create mode 100644 server/handler/server.go delete mode 100644 server/handler/site.go create mode 100644 server/handler/ws.go create mode 100644 server/pubsub/buffer.go create mode 100644 server/pubsub/channel.go create mode 100644 server/pubsub/opts.go create mode 100644 server/pubsub/pubsub.go create mode 100644 server/pubsub/subscribe.go delete mode 100644 server/queue/build_runner.go delete mode 100644 server/queue/queue.go delete mode 100644 server/queue/worker.go delete mode 100644 server/template/400.amber delete mode 100644 server/template/401.amber delete mode 100644 server/template/403.amber delete mode 100644 server/template/404.amber delete mode 100644 server/template/admin_conf.amber delete mode 100644 server/template/admin_users.amber delete mode 100644 server/template/base.amber delete mode 100644 server/template/html/400.html delete mode 100644 server/template/html/401.html delete mode 100644 server/template/html/403.html delete mode 100644 server/template/html/404.html delete mode 100644 server/template/html/admin_conf.html delete mode 100644 server/template/html/admin_users.html delete mode 100644 server/template/html/base.html delete mode 100644 server/template/html/index.html delete mode 100644 server/template/html/login.html delete mode 100644 server/template/html/repo_branch.html delete mode 100644 server/template/html/repo_commit.html delete mode 100644 server/template/html/repo_conf.html delete mode 100644 server/template/html/repo_feed.html delete mode 100644 server/template/html/user_conf.html delete mode 100644 server/template/html/user_feed.html delete mode 100644 server/template/html/user_login.html delete mode 100644 server/template/html/user_repos.html delete mode 100644 server/template/login.amber delete mode 100644 server/template/repo_branch.amber delete mode 100644 server/template/repo_commit.amber delete mode 100644 server/template/repo_conf.amber delete mode 100644 server/template/repo_feed.amber delete mode 100644 server/template/user_conf.amber delete mode 100644 server/template/user_feed.amber delete mode 100644 server/template/user_login.amber delete mode 100644 server/template/user_repos.amber create mode 100644 server/worker/dispatch.go create mode 100644 server/worker/request.go create mode 100644 server/worker/worker.go create mode 100644 shared/model/remote.go create mode 100644 shared/model/server.go diff --git a/.gitignore b/.gitignore index 951a90ba..8c04d8bc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,4 @@ drone.sublime-workspace client/client server/server -debian/drone/usr/local/bin/drone -debian/drone/usr/local/bin/droned +debian/drone/usr diff --git a/Makefile b/Makefile index 072c6126..c46ab825 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ SHA := $(shell git rev-parse --short HEAD) -all: rice amberc lessc build +all: build deps: - go get github.com/eknkc/amber/amberc + # npm install -g uglify-js + # npm install -g less go get github.com/GeertJohan/go.rice/rice go list github.com/drone/drone/... | xargs go get -t -v @@ -15,43 +16,29 @@ test: go vet ./... go test -cover -short ./... +run: + @cd server && go run main.go + clean: - @find ./ -name '*.out' | xargs rm # remove go coverage output - @find ./ -name '*.sqlite' | xargs rm # remove sqlite databases - @find ./ -name '*.rice-box.go' | xargs rm - rm -rf debian/drone/usr/local/bin/drone - rm -rf debian/drone/usr/local/bin/droned - rm -rf debian/drone.deb - rm server/server - rm client/client + @find . -name "*.out" -delete # remove go coverage output + @find . -name "*.sqlite" -delete # remove sqlite databases + @find . -name '*.rice-box.go' -delete # remove go rice files & embedded content + #@find . -name '*.css' -delete + @rm -r debian/drone/usr/local/bin debian/drone.deb server/server client/client server/template/html - #cd cmd/droned/static && rice clean - #cd cmd/droned/template && rice clean +dpkg: lessc rice build deb +# embeds content in go source code so that it is compiled +# and packaged inside the go binary file. rice: - cd server && rice embed - #cd server/template/html && rice embed - -amberc: - @for f in server/template/*.amber; do $$GOPATH/bin/amberc -pp=true "$$f" > "$${f%.amber}.html"; done - @mkdir -p server/template/html - @mv server/template/*.html server/template/html + cd server && rice embed lessc: - @lessc server/static/styles/drone.less > server/static/styles/drone.css + lessc server/app/styles/drone.less server/app/styles/drone.css + lessc --clean-css server/app/styles/drone.less server/app/styles/drone.min.css -uglify: - yui-compressor --type='css' -o 'server/static/styles/drone.min.css' server/static/styles/drone.css - -# npm install -g uglifycss -# npm install -g uglify-js -# npm install -g less - -# creates a debian package for drone -# to install `sudo dpkg -i drone.deb` -dpkg: +# creates a debian package for drone to install +# `sudo dpkg -i drone.deb` +deb: mkdir -p debian/drone/usr/local/bin - -dpkg-deb --build debian/drone - -run: - @cd server && go run main.go amber.go \ No newline at end of file + dpkg-deb --build debian/drone diff --git a/server/amber.go b/server/amber.go deleted file mode 100644 index 415d5bd4..00000000 --- a/server/amber.go +++ /dev/null @@ -1,287 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "html/template" - "reflect" -) - -var funcMap = template.FuncMap{ - "__amber_add": runtime_add, - "__amber_sub": runtime_sub, - "__amber_mul": runtime_mul, - "__amber_quo": runtime_quo, - "__amber_rem": runtime_rem, - "__amber_minus": runtime_minus, - "__amber_plus": runtime_plus, - "__amber_eql": runtime_eql, - "__amber_gtr": runtime_gtr, - "__amber_lss": runtime_lss, - - "json": runtime_json, - "unescaped": runtime_unescaped, -} - -func runtime_add(x, y interface{}) interface{} { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() + vy.Int() - case reflect.Float32, reflect.Float64: - return float64(vx.Int()) + vy.Float() - case reflect.String: - return fmt.Sprintf("%d%s", vx.Int(), vy.String()) - } - } - case reflect.Float32, reflect.Float64: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Float() + float64(vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.Float() + vy.Float() - case reflect.String: - return fmt.Sprintf("%f%s", vx.Float(), vy.String()) - } - } - case reflect.String: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return fmt.Sprintf("%s%d", vx.String(), vy.Int()) - case reflect.Float32, reflect.Float64: - return fmt.Sprintf("%s%f", vx.String(), vy.Float()) - case reflect.String: - return fmt.Sprintf("%s%s", vx.String(), vy.String()) - } - } - } - - return "" -} - -func runtime_sub(x, y interface{}) interface{} { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() - vy.Int() - case reflect.Float32, reflect.Float64: - return float64(vx.Int()) - vy.Float() - } - } - case reflect.Float32, reflect.Float64: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Float() - float64(vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.Float() - vy.Float() - } - } - } - - return "" -} - -func runtime_mul(x, y interface{}) interface{} { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() * vy.Int() - case reflect.Float32, reflect.Float64: - return float64(vx.Int()) * vy.Float() - } - } - case reflect.Float32, reflect.Float64: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Float() * float64(vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.Float() * vy.Float() - } - } - } - - return "" -} - -func runtime_quo(x, y interface{}) interface{} { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() / vy.Int() - case reflect.Float32, reflect.Float64: - return float64(vx.Int()) / vy.Float() - } - } - case reflect.Float32, reflect.Float64: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Float() / float64(vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.Float() / vy.Float() - } - } - } - - return "" -} - -func runtime_rem(x, y interface{}) interface{} { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() % vy.Int() - } - } - } - - return "" -} - -func runtime_minus(x interface{}) interface{} { - vx := reflect.ValueOf(x) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return -vx.Int() - case reflect.Float32, reflect.Float64: - return -vx.Float() - } - - return "" -} - -func runtime_plus(x interface{}) interface{} { - vx := reflect.ValueOf(x) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return +vx.Int() - case reflect.Float32, reflect.Float64: - return +vx.Float() - } - - return "" -} - -func runtime_eql(x, y interface{}) bool { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() == vy.Int() - case reflect.Float32, reflect.Float64: - return float64(vx.Int()) == vy.Float() - case reflect.String: - return fmt.Sprintf("%d", vx.Int()) == vy.String() - } - } - case reflect.Float32, reflect.Float64: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Float() == float64(vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.Float() == vy.Float() - case reflect.String: - return fmt.Sprintf("%f", vx.Float()) == vy.String() - } - } - case reflect.String: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.String() == fmt.Sprintf("%d", vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.String() == fmt.Sprintf("%f", vy.Float()) - case reflect.String: - return vx.String() == fmt.Sprintf("%s", vy.String()) - } - } - case reflect.Bool: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Bool() && vy.Int() != 0 - case reflect.Bool: - return vx.Bool() == vy.Bool() - } - } - } - - return false -} - -func runtime_lss(x, y interface{}) bool { - vx, vy := reflect.ValueOf(x), reflect.ValueOf(y) - switch vx.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Int() < vy.Int() - case reflect.Float32, reflect.Float64: - return float64(vx.Int()) < vy.Float() - case reflect.String: - return fmt.Sprintf("%d", vx.Int()) < vy.String() - } - } - case reflect.Float32, reflect.Float64: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.Float() < float64(vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.Float() < vy.Float() - case reflect.String: - return fmt.Sprintf("%f", vx.Float()) < vy.String() - } - } - case reflect.String: - { - switch vy.Kind() { - case reflect.Int, reflect.Int32, reflect.Int64, reflect.Int16, reflect.Int8: - return vx.String() < fmt.Sprintf("%d", vy.Int()) - case reflect.Float32, reflect.Float64: - return vx.String() < fmt.Sprintf("%f", vy.Float()) - case reflect.String: - return vx.String() < vy.String() - } - } - } - - return false -} - -func runtime_gtr(x, y interface{}) bool { - return !runtime_lss(x, y) && !runtime_eql(x, y) -} - -func runtime_json(x interface{}) (res string, err error) { - bres, err := json.Marshal(x) - res = string(bres) - return -} - -func runtime_unescaped(x string) interface{} { - return template.HTML(x) -} diff --git a/server/static/images/favicon.ico b/server/app/favicon.ico similarity index 100% rename from server/static/images/favicon.ico rename to server/app/favicon.ico diff --git a/server/static/images/favicon.png b/server/app/favicon.png similarity index 100% rename from server/static/images/favicon.png rename to server/app/favicon.png diff --git a/server/app/index.html b/server/app/index.html new file mode 100644 index 00000000..a9b22e7a --- /dev/null +++ b/server/app/index.html @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/app/robots.txt b/server/app/robots.txt new file mode 100644 index 00000000..6b0157e2 --- /dev/null +++ b/server/app/robots.txt @@ -0,0 +1,3 @@ +# robotstxt.org + +User-agent: * \ No newline at end of file diff --git a/server/app/scripts/app.js b/server/app/scripts/app.js new file mode 100644 index 00000000..7fd7bdbc --- /dev/null +++ b/server/app/scripts/app.js @@ -0,0 +1,395 @@ +'use strict'; + +var app = angular.module('app', [ + 'ngRoute', + 'ui.filters' +]); + +app.config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) { + $routeProvider.when('/', { + templateUrl: '/views/index.html', + controller: 'HomeController', + title: 'Dashboard', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/login', { + templateUrl: '/views/login.html', + title: 'Login', + }) + .when('/setup', { + templateUrl: '/views/setup.html', + controller: 'SetupController', + title: 'Setup' + }) + .when('/setup/:remote', { + templateUrl: '/views/setup.html', + controller: 'SetupController', + title: 'Setup' + }) + .when('/account/profile', { + templateUrl: '/views/account.html', + controller: 'UserController', + title: 'Profile', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/account/repos', { + templateUrl: '/views/repo_list.html', + controller: 'AccountReposController', + title: 'Repositories', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/admin/users', { + templateUrl: '/views/sys_users.html', + controller: 'UsersController', + title: 'System Users', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/admin/settings', { + templateUrl: '/views/sys_config.html', + controller: 'ConfigController', + title: 'System Settings', + resolve: { + user: function(authService) { + return authService.getUser(); + }, + conf: function(confService) { + return confService.getConfig(); + } + } + }) + .when('/:remote/:owner/:name/settings', { + templateUrl: '/views/repo_conf.html', + controller: 'RepoConfigController', + title: 'Repository Settings', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/:remote/:owner/:name/:branch/:commit', { + templateUrl: '/views/commit.html', + controller: 'CommitController', + title: 'Recent Commits', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/:remote/:owner/:name/:branch', { + templateUrl: '/views/branch.html', + controller: 'BranchController', + title: 'Recent Commits', + resolve: { + user: function(authService) { + return authService.getUser(); + } + } + }) + .when('/:remote/:owner/:name', { + templateUrl: '/views/repo.html', + controller: 'RepoController', + title: 'Recent Commits', + resolve: { + user: function(authService) { + return authService.getUser(); + }, + repo: function($route, repoService) { + var remote = $route.current.params.remote; + var owner = $route.current.params.owner; + var name = $route.current.params.name; + return repoService.getRepo(remote, owner, name); + } + } + }); + + // use the HTML5 History API + $locationProvider.html5Mode(true); +}]); + +/* Directives */ + +/* also see https://coderwall.com/p/vcfo4q */ +app.run(['$location', '$rootScope', '$routeParams', function($location, $rootScope, $routeParams) { + $rootScope.$on('$routeChangeSuccess', function (event, current, previous) { + document.title = current.$$route.title + ' · drone.io'; + }); +}]); + + + + +/* Controllers */ + + + + +app.controller("AccountReposController", function($scope, $http, user) { + + $scope.user = user; + + // get the user details + $http({method: 'GET', url: '/v1/user/repos'}). + success(function(data, status, headers, config) { + $scope.repos = (typeof data==='string')?[]:data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + $scope.active=""; + $scope.remote=""; + $scope.byActive = function(entry){ + switch (true) { + case $scope.active == "true" && !entry.active: return false; + case $scope.active == "false" && entry.active: return false; + } + return true; + }; + $scope.byRemote = function(entry){ + return $scope.remote == "" || $scope.remote == entry.remote; + }; +}); + + + +app.controller("ConfigController", function($scope, $http, user) { + + $scope.user = user; + + $http({method: 'GET', url: '/v1/config'}). + success(function(data, status, headers, config) { + $scope.config = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); +}); + +app.controller("RepoConfigController", function($scope, $http, $routeParams, user) { + + $scope.user = user; + + var remote = $routeParams.remote; + var owner = $routeParams.owner; + var name = $routeParams.name; + + + + // load the repo meta-data + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name}). + success(function(data, status, headers, config) { + $scope.repo = data; + $scope.repoTemp = { + pull_requests : $scope.repo.pull_requests, + post_commits : $scope.repo.post_commits, + params : $scope.repo.params, + timeout : $scope.repo.timeout, + privileged : $scope.repo.privileged + }; + + $scope.badgeMarkdown = badgeMarkdown(data.remote+"/"+data.owner+"/"+data.name) + $scope.badgeMarkup = badgeMarkup(data.remote+"/"+data.owner+"/"+data.name) + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + $scope.save = function() { + // request to create a new repository + $http({method: 'PUT', url: '/v1/repos/'+remote+'/'+owner+"/"+name, data: $scope.repoTemp }). + success(function(data, status, headers, config) { + delete $scope.failure; + $scope.repo = data; + }). + error(function(data, status, headers, config) { + $scope.failure = data; + }); + }; + $scope.cancel = function() { + delete $scope.failure; + $scope.repoTemp = { + pull_requests : $scope.repo.pull_requests, + post_commits : $scope.repo.post_commits, + params : $scope.repo.params, + timeout : $scope.repo.timeout, + privileged : $scope.repo.privileged + }; + }; +}); + +function badgeMarkdown(repo) { + var scheme = window.location.protocol; + var host = window.location.host; + return '[![Build Status]('+scheme+'//'+host+'/v1/badge/'+repo+'/status.svg?branch=master)]('+scheme+'//'+host+'/'+repo+')' +} + +function badgeMarkup(repo) { + var scheme = window.location.protocol; + var host = window.location.host; + return '' +} + +app.controller("RepoController", function($scope, $http, $routeParams, user, repo) { + $scope.user = user; + $scope.repo = repo; + + // load the repo branch list + $http({method: 'GET', url: '/v1/repos/'+repo.host+'/'+repo.owner+"/"+repo.name+"/branches"}). + success(function(data, status, headers, config) { + $scope.branches = (typeof data==='string')?[]:data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + // load the repo commit feed + $http({method: 'GET', url: '/v1/repos/'+repo.host+'/'+repo.owner+"/"+repo.name+"/feed"}). + success(function(data, status, headers, config) { + $scope.commits = (typeof data==='string')?[]:data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + $scope.activate = function() { + // request to create a new repository + $http({method: 'POST', url: '/v1/repos/'+repo.host+'/'+repo.owner+"/"+repo.name }). + success(function(data, status, headers, config) { + $scope.repo = data; + }). + error(function(data, status, headers, config) { + $scope.failure = data; + }); + }; + + $scope.options={ + barColor:"#40C598", + trackColor:'#EEEEEE', + scaleColor:false, + lineWidth:10, + lineCap:'butt', + size:130 + }; +}); + +app.controller("BranchController", function($scope, $http, $routeParams, user) { + + $scope.user = user; + var remote = $routeParams.remote; + var owner = $routeParams.owner; + var name = $routeParams.name; + var branch = $routeParams.branch; + $scope.branch = branch; + + // load the repo meta-data + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name}). + success(function(data, status, headers, config) { + $scope.repo = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + // load the repo branch list + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name+"/branches"}). + success(function(data, status, headers, config) { + $scope.branches = (typeof data==='string')?[]:data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + // load the repo commit feed + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name+"/branches/"+branch+"/commits"}). + success(function(data, status, headers, config) { + $scope.commits = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); +}); + +app.controller("CommitController", function($scope, $http, $routeParams, user) { + + $scope.user = user; + var remote = $routeParams.remote; + var owner = $routeParams.owner; + var name = $routeParams.name; + var branch = $routeParams.branch; + var commit = $routeParams.commit; + + // load the repo meta-data + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name}). + success(function(data, status, headers, config) { + $scope.repo = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + // load the repo commit data + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name+"/branches/"+branch+"/commits/"+commit}). + success(function(data, status, headers, config) { + $scope.commit = data; + $scope.coverage=45; + $scope.passing=100; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + // load the repo build data + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name+"/branches/"+branch+"/commits/"+commit+"/builds/1"}). + success(function(data, status, headers, config) { + $scope.build = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + // load the repo build stdout + $http({method: 'GET', url: '/v1/repos/'+remote+'/'+owner+"/"+name+"/branches/"+branch+"/commits/"+commit+"/builds/1/console"}). + success(function(data, status, headers, config) { + $scope.console = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + + $scope.options={ + barColor:"#40C598", + trackColor:'#EEEEEE', + scaleColor:false, + lineWidth:10, + lineCap:'butt', + size:130 + }; + +}); + +function barColor(percent) { + switch(true) { + case percent > 80: return "#40C598"; + case percent < 50: return "rgba(189, 54, 47, 0.8)"; + default: return "#f0ad4e"; + } +} \ No newline at end of file diff --git a/server/app/scripts/commit_updates.js b/server/app/scripts/commit_updates.js new file mode 100644 index 00000000..6493f355 --- /dev/null +++ b/server/app/scripts/commit_updates.js @@ -0,0 +1,98 @@ +;// Live commit updates + +if(typeof(Drone) === 'undefined') { Drone = {}; } + +(function () { + Drone.CommitUpdates = function(socket) { + if(typeof(socket) === "string") { + var url = [(window.location.protocol == 'https:' ? 'wss' : 'ws'), + '://', + window.location.host, + socket].join('') + this.socket = new WebSocket(url); + } else { + this.socket = socket; + } + + this.lineFormatter = new Drone.LineFormatter(); + this.attach(); + } + + Drone.CommitUpdates.prototype = { + lineBuffer: "", + autoFollow: false, + + startOutput: function(el) { + if(typeof(el) === 'string') { + this.el = document.getElementById(el); + } else { + this.el = el; + } + + if(!this.reqId) { + this.updateScreen(); + } + }, + + stopOutput: function() { + this.stoppingRefresh = true; + }, + + attach: function() { + this.socket.onopen = this.onOpen; + this.socket.onerror = this.onError; + this.socket.onmessage = this.onMessage.bind(this); + this.socket.onclose = this.onClose; + }, + + updateScreen: function() { + if(this.lineBuffer.length > 0) { + this.el.innerHTML += this.lineBuffer; + this.lineBuffer = ''; + + if (this.autoFollow) { + window.scrollTo(0, document.body.scrollHeight); + } + } + + if(this.stoppingRefresh) { + this.stoppingRefresh = false; + } else { + window.requestAnimationFrame(this.updateScreen.bind(this)); + } + }, + + onOpen: function() { + console.log('output websocket open'); + }, + + onError: function(e) { + console.log('websocket error: ' + e); + }, + + onMessage: function(e) { + this.lineBuffer += this.lineFormatter.format(e.data); + }, + + onClose: function(e) { + console.log('output websocket closed: ' + JSON.stringify(e)); + window.location.reload(); + } + }; + + // Polyfill rAF for older browsers + window.requestAnimationFrame = window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + function(callback, element) { + return window.setTimeout(function() { + callback(+new Date()); + }, 1000 / 60); + }; + + window.cancelRequestAnimationFrame = window.cancelRequestAnimationFrame || + window.cancelWebkitRequestAnimationFrame || + function(fn) { + window.clearTimeout(fn); + }; + +})(); diff --git a/server/app/scripts/controllers/build.js b/server/app/scripts/controllers/build.js new file mode 100644 index 00000000..ad9a93a7 --- /dev/null +++ b/server/app/scripts/controllers/build.js @@ -0,0 +1 @@ +'use strict'; diff --git a/server/app/scripts/controllers/conf.js b/server/app/scripts/controllers/conf.js new file mode 100644 index 00000000..ad9a93a7 --- /dev/null +++ b/server/app/scripts/controllers/conf.js @@ -0,0 +1 @@ +'use strict'; diff --git a/server/app/scripts/controllers/home.js b/server/app/scripts/controllers/home.js new file mode 100644 index 00000000..39370b12 --- /dev/null +++ b/server/app/scripts/controllers/home.js @@ -0,0 +1,14 @@ +'use strict'; + +angular.module('app').controller("HomeController", function($scope, $http, user) { + + $scope.user = user; + + $http({method: 'GET', url: '/v1/user/feed'}). + success(function(data, status, headers, config) { + $scope.feed = (typeof data==='string')?[]:data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); +}); diff --git a/server/app/scripts/controllers/repo.js b/server/app/scripts/controllers/repo.js new file mode 100644 index 00000000..ad9a93a7 --- /dev/null +++ b/server/app/scripts/controllers/repo.js @@ -0,0 +1 @@ +'use strict'; diff --git a/server/app/scripts/controllers/setup.js b/server/app/scripts/controllers/setup.js new file mode 100644 index 00000000..9b86e077 --- /dev/null +++ b/server/app/scripts/controllers/setup.js @@ -0,0 +1,36 @@ +'use strict'; + +angular.module('app').controller("SetupController", function($scope, $http, $routeParams) { + + // create a remote that will be populated + // and persisted to the database. + $scope.remote = {}; + $scope.remote.type = $routeParams.remote; + $scope.remote.register = true; + switch($scope.remote.type) { + case undefined: + case 'github.com': + $scope.remote.type = "github.com" + $scope.remote.url = "https://github.com"; + $scope.remote.api = "https://api.github.com"; + break; + case 'bitbucket.org': + $scope.remote.url = "https://bitbucket.org"; + $scope.remote.api = "https://bitbucket.org"; + break; + } + + $scope.save = function() { + // request to create a new repository + $http({method: 'POST', url: '/v1/remotes', data: $scope.remote }). + success(function(data, status, headers, config) { + delete $scope.failure; + $scope.remote = data; + console.log('success', $scope.remote); + }). + error(function(data, status, headers, config) { + $scope.failure = data; + console.log('failure', $scope.failure); + }); + }; +}); \ No newline at end of file diff --git a/server/app/scripts/controllers/user.js b/server/app/scripts/controllers/user.js new file mode 100644 index 00000000..72d76525 --- /dev/null +++ b/server/app/scripts/controllers/user.js @@ -0,0 +1,38 @@ +'use strict'; + +angular.module('app').controller("UserController", function($scope, $http, user) { + + $scope.user = user; + + // get the user details + $http({method: 'GET', url: '/v1/user'}). + success(function(data, status, headers, config) { + $scope.user = data; + $scope.userTemp = { + email : $scope.user.email, + name : $scope.user.name + }; + }). + error(function(data, status, headers, config) { + console.log(data); + }); + + $scope.save = function() { + // request to create a new repository + $http({method: 'PUT', url: '/v1/user', data: $scope.userTemp }). + success(function(data, status, headers, config) { + delete $scope.failure; + $scope.user = data; + }). + error(function(data, status, headers, config) { + $scope.failure = data; + }); + }; + $scope.cancel = function() { + delete $scope.failure; + $scope.userTemp = { + email : $scope.user.email, + name : $scope.user.name + }; + }; +}); \ No newline at end of file diff --git a/server/app/scripts/controllers/users.js b/server/app/scripts/controllers/users.js new file mode 100644 index 00000000..010d0190 --- /dev/null +++ b/server/app/scripts/controllers/users.js @@ -0,0 +1,14 @@ +'use strict'; + +angular.module('app').controller("UsersController", function($scope, $http, user) { + + $scope.user = user; + + $http({method: 'GET', url: '/v1/users'}). + success(function(data, status, headers, config) { + $scope.users = data; + }). + error(function(data, status, headers, config) { + console.log(data); + }); +}); \ No newline at end of file diff --git a/server/app/scripts/filters/filters.js b/server/app/scripts/filters/filters.js new file mode 100644 index 00000000..437438a5 --- /dev/null +++ b/server/app/scripts/filters/filters.js @@ -0,0 +1,62 @@ +'use strict'; + +angular.module('app').filter('gravatar', function() { + return function(gravatar) { + return "https://secure.gravatar.com/avatar/"+gravatar+"?s=32&d=mm" + } +}); + +angular.module('app').filter('fromNow', function() { + return function(date) { + return moment(new Date(date*1000)).fromNow(); + } +}); + +angular.module('app').filter('toDuration', function() { + return function(seconds) { + return moment.duration(seconds, "seconds").humanize(); + } +}); + +angular.module('app').filter('toDate', function() { + return function(date) { + return moment(new Date(date*1000)).format('ll'); + } +}); + +angular.module('app').filter('fullName', function() { + return function(repo) { + return repo.owner+"/"+repo.name; + } +}); + +angular.module('app').filter('fullPath', function() { + return function(repo) { + return repo.remote+"/"+repo.owner+"/"+repo.name; + } +}); + +angular.module('app').filter('shortHash', function() { + return function(sha) { + if (!sha) { return ""; } + return sha.substr(0,10) + } +}); + +angular.module('app').filter('badgeMarkdown', function() { + return function(repo) { + var scheme = window.location.protocol; + var host = window.location.host; + var path = repo.host+'/'+repo.owner+'/'+repo.name; + return '[![Build Status]('+scheme+'//'+host+'/v1/badge/'+path+'/status.svg?branch=master)]('+scheme+'//'+host+'/'+path+')' + } +}); + +angular.module('app').filter('badgeMarkup', function() { + return function(repo) { + var scheme = window.location.protocol; + var host = window.location.host; + var path = repo.host+'/'+repo.owner+'/'+repo.name; + return '' + } +}); \ No newline at end of file diff --git a/server/static/scripts/line_formatter.js b/server/app/scripts/line_formatter.js similarity index 100% rename from server/static/scripts/line_formatter.js rename to server/app/scripts/line_formatter.js diff --git a/server/static/scripts/main.js b/server/app/scripts/main.js similarity index 100% rename from server/static/scripts/main.js rename to server/app/scripts/main.js diff --git a/server/app/scripts/services/auth.js b/server/app/scripts/services/auth.js new file mode 100644 index 00000000..8aab0e7c --- /dev/null +++ b/server/app/scripts/services/auth.js @@ -0,0 +1,34 @@ +'use strict;' + +angular.module('app').service('authService', function($q, $http) { + return{ + user : null, + // getUser will retrieve the currently authenticated + // user from the session. If no user is found a 401 + // Not Authorized status will be returned. + getUser : function() { + var _this = this; + var defer = $q.defer(); + + // if the user is already authenticated + if (_this.user != null) { + defer.resolve(_this.user); + } + + // else we need to fetch from the server + $http({method: 'GET', url: '/v1/user'}). + success(function(data) { + _this.user=data; + defer.resolve(_this.user); + }). + error(function(data, status) { + _this.user=null; + defer.resolve(); + }); + + // returns a promise that this will complete + // at some future time. + return defer.promise; + } + } +}); \ No newline at end of file diff --git a/server/app/scripts/services/conf.js b/server/app/scripts/services/conf.js new file mode 100644 index 00000000..1e77094f --- /dev/null +++ b/server/app/scripts/services/conf.js @@ -0,0 +1,14 @@ +'use strict'; + +angular.module('app').service('confService', function($q, $http) { + return{ + getConfig : function() { + var defer = $q.defer(); + var route = '/v1/config'; + $http.get(route).success(function(data){ + defer.resolve(data); + }); + return defer.promise; + } + } +}); \ No newline at end of file diff --git a/server/app/scripts/services/repo.js b/server/app/scripts/services/repo.js new file mode 100644 index 00000000..5ba225e3 --- /dev/null +++ b/server/app/scripts/services/repo.js @@ -0,0 +1,14 @@ +'use strict'; + +angular.module('app').service('repoService', function($q, $http) { + return{ + getRepo : function(host, owner, name) { + var defer = $q.defer(); + var route = '/v1/repos/'+host+'/'+owner+'/'+name; + $http.get(route).success(function(data){ + defer.resolve(data); + }); + return defer.promise; + } + } +}); diff --git a/server/app/scripts/services/user.js b/server/app/scripts/services/user.js new file mode 100644 index 00000000..bcca30ec --- /dev/null +++ b/server/app/scripts/services/user.js @@ -0,0 +1,31 @@ +'use strict'; + +angular.module('app').service('userService', function($q, $http, $timeout) { + return{ + user : null, + getCurrent : function() { + var _this = this; + var defer = $q.defer(); + + // if the user is already authenticated + if (_this.user != null) { + defer.resolve(_this.user); + } + + // else we need to fetch from the server + $http({method: 'GET', url: '/v1/user'}). + then(function(data) { + _this.user=data; + defer.resolve(_this.user); + }). + error(function(data, status) { + _this.user=null; + defer.resolve(); + }); + + // returns a promise that this will complete + // at some future time. + return defer.promise; + } + } +}); \ No newline at end of file diff --git a/server/static/scripts/commit_updates.js b/server/app/scripts/util/commit_updates.js similarity index 100% rename from server/static/scripts/commit_updates.js rename to server/app/scripts/util/commit_updates.js diff --git a/server/app/scripts/util/line_formatter.js b/server/app/scripts/util/line_formatter.js new file mode 100644 index 00000000..754e6cee --- /dev/null +++ b/server/app/scripts/util/line_formatter.js @@ -0,0 +1,66 @@ +;// Format ANSI to HTML + +if(typeof(Drone) === 'undefined') { Drone = {}; } + +(function() { + Drone.LineFormatter = function() {}; + + Drone.LineFormatter.prototype = { + regex: /\u001B\[([0-9]+;?)*[Km]/g, + styles: [], + + format: function(s) { + // Check for newline and early exit? + s = s.replace(//g, ">"); + + var output = ""; + var current = 0; + while (m = this.regex.exec(s)) { + var part = s.substring(current, m.index); + current = this.regex.lastIndex; + + var token = s.substr(m.index, this.regex.lastIndex - m.index); + var code = token.substr(2, token.length-2); + + var pre = ""; + var post = ""; + + switch (code) { + case 'm': + case '0m': + var len = this.styles.length; + for (var i=0; i < len; i++) { + this.styles.pop(); + post += "" + } + break; + case '30;42m': pre = ''; break; + case '36m': + case '36;1m': pre = ''; break; + case '31m': + case '31;31m': pre = ''; break; + case '33m': + case '33;33m': pre = ''; break; + case '32m': + case '0;32m': pre = ''; break; + case '90m': pre = ''; break; + case 'K': + case '0K': + case '1K': + case '2K': break; + } + + if (pre !== "") { + this.styles.push(pre); + } + + output += part + pre + post; + } + + var part = s.substring(current, s.length); + output += part; + return output; + } + }; +})(); diff --git a/server/static/styles/base/clearfix.less b/server/app/styles/base/clearfix.less similarity index 100% rename from server/static/styles/base/clearfix.less rename to server/app/styles/base/clearfix.less diff --git a/server/static/styles/base/overflow.less b/server/app/styles/base/overflow.less similarity index 100% rename from server/static/styles/base/overflow.less rename to server/app/styles/base/overflow.less diff --git a/server/static/styles/base/variables.less b/server/app/styles/base/variables.less similarity index 100% rename from server/static/styles/base/variables.less rename to server/app/styles/base/variables.less diff --git a/server/static/styles/drone.css b/server/app/styles/drone.css similarity index 100% rename from server/static/styles/drone.css rename to server/app/styles/drone.css diff --git a/server/static/styles/drone.css.bak b/server/app/styles/drone.css.bak similarity index 100% rename from server/static/styles/drone.css.bak rename to server/app/styles/drone.css.bak diff --git a/server/static/styles/drone.less b/server/app/styles/drone.less similarity index 100% rename from server/static/styles/drone.less rename to server/app/styles/drone.less diff --git a/server/static/styles/drone.min.css b/server/app/styles/drone.min.css similarity index 100% rename from server/static/styles/drone.min.css rename to server/app/styles/drone.min.css diff --git a/server/static/styles/general/angular.less b/server/app/styles/general/angular.less similarity index 100% rename from server/static/styles/general/angular.less rename to server/app/styles/general/angular.less diff --git a/server/static/styles/general/buttons.less b/server/app/styles/general/buttons.less similarity index 100% rename from server/static/styles/general/buttons.less rename to server/app/styles/general/buttons.less diff --git a/server/static/styles/general/forms.less b/server/app/styles/general/forms.less similarity index 100% rename from server/static/styles/general/forms.less rename to server/app/styles/general/forms.less diff --git a/server/static/styles/general/resets.less b/server/app/styles/general/resets.less similarity index 100% rename from server/static/styles/general/resets.less rename to server/app/styles/general/resets.less diff --git a/server/static/styles/layout/layout.less b/server/app/styles/layout/layout.less similarity index 100% rename from server/static/styles/layout/layout.less rename to server/app/styles/layout/layout.less diff --git a/server/static/styles/modules/alert.less b/server/app/styles/modules/alert.less similarity index 100% rename from server/static/styles/modules/alert.less rename to server/app/styles/modules/alert.less diff --git a/server/static/styles/modules/article.less b/server/app/styles/modules/article.less similarity index 100% rename from server/static/styles/modules/article.less rename to server/app/styles/modules/article.less diff --git a/server/static/styles/modules/authors.less b/server/app/styles/modules/authors.less similarity index 100% rename from server/static/styles/modules/authors.less rename to server/app/styles/modules/authors.less diff --git a/server/static/styles/modules/branches.less b/server/app/styles/modules/branches.less similarity index 100% rename from server/static/styles/modules/branches.less rename to server/app/styles/modules/branches.less diff --git a/server/static/styles/modules/build.less b/server/app/styles/modules/build.less similarity index 100% rename from server/static/styles/modules/build.less rename to server/app/styles/modules/build.less diff --git a/server/static/styles/modules/card.less b/server/app/styles/modules/card.less similarity index 100% rename from server/static/styles/modules/card.less rename to server/app/styles/modules/card.less diff --git a/server/static/styles/modules/charts.less b/server/app/styles/modules/charts.less similarity index 100% rename from server/static/styles/modules/charts.less rename to server/app/styles/modules/charts.less diff --git a/server/static/styles/modules/form-search.less b/server/app/styles/modules/form-search.less similarity index 100% rename from server/static/styles/modules/form-search.less rename to server/app/styles/modules/form-search.less diff --git a/server/static/styles/modules/header.less b/server/app/styles/modules/header.less similarity index 100% rename from server/static/styles/modules/header.less rename to server/app/styles/modules/header.less diff --git a/server/static/styles/modules/list-activity.less b/server/app/styles/modules/list-activity.less similarity index 100% rename from server/static/styles/modules/list-activity.less rename to server/app/styles/modules/list-activity.less diff --git a/server/static/styles/modules/list-commits.less b/server/app/styles/modules/list-commits.less similarity index 100% rename from server/static/styles/modules/list-commits.less rename to server/app/styles/modules/list-commits.less diff --git a/server/static/styles/modules/list-repos.less b/server/app/styles/modules/list-repos.less similarity index 100% rename from server/static/styles/modules/list-repos.less rename to server/app/styles/modules/list-repos.less diff --git a/server/static/styles/modules/list-users.less b/server/app/styles/modules/list-users.less similarity index 100% rename from server/static/styles/modules/list-users.less rename to server/app/styles/modules/list-users.less diff --git a/server/static/styles/modules/nav.less b/server/app/styles/modules/nav.less similarity index 100% rename from server/static/styles/modules/nav.less rename to server/app/styles/modules/nav.less diff --git a/server/static/styles/modules/section.less b/server/app/styles/modules/section.less similarity index 100% rename from server/static/styles/modules/section.less rename to server/app/styles/modules/section.less diff --git a/server/static/styles/modules/stdout.less b/server/app/styles/modules/stdout.less similarity index 100% rename from server/static/styles/modules/stdout.less rename to server/app/styles/modules/stdout.less diff --git a/server/static/styles/modules/switch.less b/server/app/styles/modules/switch.less similarity index 100% rename from server/static/styles/modules/switch.less rename to server/app/styles/modules/switch.less diff --git a/server/static/styles/modules/user-account.less b/server/app/styles/modules/user-account.less similarity index 100% rename from server/static/styles/modules/user-account.less rename to server/app/styles/modules/user-account.less diff --git a/server/static/styles/vendor/font-awesome.less b/server/app/styles/vendor/font-awesome.less similarity index 100% rename from server/static/styles/vendor/font-awesome.less rename to server/app/styles/vendor/font-awesome.less diff --git a/server/static/styles/vendor/pure-base.less b/server/app/styles/vendor/pure-base.less similarity index 100% rename from server/static/styles/vendor/pure-base.less rename to server/app/styles/vendor/pure-base.less diff --git a/server/static/styles/vendor/pure-forms.less b/server/app/styles/vendor/pure-forms.less similarity index 100% rename from server/static/styles/vendor/pure-forms.less rename to server/app/styles/vendor/pure-forms.less diff --git a/server/static/styles/vendor/pure-grids.less b/server/app/styles/vendor/pure-grids.less similarity index 100% rename from server/static/styles/vendor/pure-grids.less rename to server/app/styles/vendor/pure-grids.less diff --git a/server/static/styles/vendor/pure-menus.less b/server/app/styles/vendor/pure-menus.less similarity index 100% rename from server/static/styles/vendor/pure-menus.less rename to server/app/styles/vendor/pure-menus.less diff --git a/server/app/views/account.html b/server/app/views/account.html new file mode 100644 index 00000000..4eee9a3d --- /dev/null +++ b/server/app/views/account.html @@ -0,0 +1,64 @@ +
+ +

User Account

+ +
+ + +
+ What to show here? +
+ + +
+ +
+ + {{ user.remote }} +
+ +
+ + {{ user.login }} +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + {{ user.created_at | fromNow }} +
+ +
+ + {{ user.updated_at | fromNow }} +
+ +
+ + {{ user.synced_at | fromNow }} +
+ +
+ {{failure}} +
+ +
+ + +
+
+
\ No newline at end of file diff --git a/server/app/views/branch.html b/server/app/views/branch.html new file mode 100644 index 00000000..e467ddb0 --- /dev/null +++ b/server/app/views/branch.html @@ -0,0 +1,51 @@ +
+ + +

{{ repo.name }}

+Back +Settings + +
+
+ What to show here? +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
StatusBranchShaPRAuthorMessageStartedFinished
+ + {{ commit.status }} + + + + {{ commit.branch }} + + + + {{ commit.sha | shortHash }} + + {{ commit.pull_request }}{{ commit.author }}{{ commit.message }}{{ commit.started_at | fromNow }}{{ commit.finished_at | fromNow }}
+
+
\ No newline at end of file diff --git a/server/app/views/commit.html b/server/app/views/commit.html new file mode 100644 index 00000000..40bac4fb --- /dev/null +++ b/server/app/views/commit.html @@ -0,0 +1,18 @@ +
+ + +

{{ repo.name }}

+Back +Settings + +
+
+ Commit + {{ commit.sha }} +
+ + +
+
{{ console }}
+
+
\ No newline at end of file diff --git a/server/app/views/header.html b/server/app/views/header.html new file mode 100644 index 00000000..517038dc --- /dev/null +++ b/server/app/views/header.html @@ -0,0 +1,12 @@ + + + + + + + + + +
Home{{ user.login }}
+ Login +
\ No newline at end of file diff --git a/server/app/views/home.html b/server/app/views/home.html new file mode 100644 index 00000000..35327baf --- /dev/null +++ b/server/app/views/home.html @@ -0,0 +1 @@ +dashboard.html \ No newline at end of file diff --git a/server/app/views/index.html b/server/app/views/index.html new file mode 100644 index 00000000..d5ce24b7 --- /dev/null +++ b/server/app/views/index.html @@ -0,0 +1,53 @@ +
+ +

Dashboard

+ +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + +
NameStatusBranchShaPRAuthorMessageStartedFinished
+ + {{ commit.owner }} + / + {{ commit.name }} + + + + {{ commit.status }} + + + + {{ commit.branch }} + + + + {{ commit.sha | shortHash }} + + {{ commit.pull_request }}{{ commit.author }}{{ commit.message }}{{ commit.started_at | fromNow }}{{ commit.finished_at | fromNow }}
+
+
\ No newline at end of file diff --git a/server/app/views/login.html b/server/app/views/login.html new file mode 100644 index 00000000..ddcf9eb4 --- /dev/null +++ b/server/app/views/login.html @@ -0,0 +1,19 @@ +
+ +

Login

+ +
+ GitHub +
+ + +
+ Stash +
+
+ GitLab +
diff --git a/server/app/views/repo.html b/server/app/views/repo.html new file mode 100644 index 00000000..86c73c10 --- /dev/null +++ b/server/app/views/repo.html @@ -0,0 +1,71 @@ +
+ + +

{{ repo.name }}

+ + +
+ This repo is not active +
URL: {{ repo.url }}
+
Private: {{ repo.private }}
+
remote: {{ repo.remote }}
+
owner: {{ repo.owner }}
+
name: {{ repo.name }}
+ +
+ +
+
+ +
+
+ Settings
+ Recently Updated Branches + +
+ + +
+ + + + + + + + + + + + + + + + + + + + + + +
StatusBranchShaPRAuthorMessageStartedFinished
+ + {{ commit.status }} + + + + {{ commit.branch }} + + + + {{ commit.sha | shortHash }} + + {{ commit.pull_request }}{{ commit.author }}{{ commit.message }}{{ commit.started_at | fromNow }}{{ commit.finished_at | fromNow }}
+
+
\ No newline at end of file diff --git a/server/app/views/repo_conf.html b/server/app/views/repo_conf.html new file mode 100644 index 00000000..19fb283a --- /dev/null +++ b/server/app/views/repo_conf.html @@ -0,0 +1,71 @@ +
+ + +

{{ repo.name }}

+

Config

+ +Back + +
+ + +
+ + + + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
--
+
+ +
+ + +
+ +
+ + +
+ +
+ + {{ repo.url }} +
+ +
+ + {{ repo.created_at | fromNow }} +
+ +
+ + {{ repo.updated_at | fromNow }} +
+ +
+ {{failure}} +
+ +
+ + +
+
+
\ No newline at end of file diff --git a/server/app/views/setup.html b/server/app/views/setup.html new file mode 100644 index 00000000..2a7ef2ad --- /dev/null +++ b/server/app/views/setup.html @@ -0,0 +1,57 @@ +

First Time Setup

+ +
+ + + +
+ +
+ + + +
+ +
+ +
+ + + +
+
+ +
+ +
+ + + +
+
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
\ No newline at end of file diff --git a/server/app/views/sys_config.html b/server/app/views/sys_config.html new file mode 100644 index 00000000..956fd08d --- /dev/null +++ b/server/app/views/sys_config.html @@ -0,0 +1,115 @@ +
+ + +
+
+ +
+ + +
+ + + + + + + + + + + + + +
Host{{ config.host }}
Scheme{{ config.scheme }}
Registration{{ config.registration }}
+ +

GitHub

+ + + + + + + + + + + + + + + + + + + + + +
Enabled{{ config.github.enabled }}
URL{{ config.github.url }}
API{{ config.github.api }}
Client{{ config.github.client }}
Secret{{ config.github.secret }}
+ +

GitHub Enterprise

+ + + + + + + + + + + + + + + + + + + + + +
Enabled{{ config.githubEnterprise.enabled }}
URL{{ config.githubEnterprise.url }}
API{{ config.githubEnterprise.api }}
Client{{ config.githubEnterprise.client }}
Secret{{ config.githubEnterprise.secret }}
+ +

Bitbucket

+ + + + + + + + + + + + + + + + + + + + + +
Enabled{{ config.bitbucket.enabled }}
URL{{ config.bitbucket.url }}
API{{ config.bitbucket.api }}
Client{{ config.bitbucket.client }}
Secret{{ config.bitbucket.secret }}
+ +

Gitlab

+ + + + + + + + + +
Enabled{{ config.bitbucket.enabled }}
URL{{ config.bitbucket.url }}
+
+
\ No newline at end of file diff --git a/server/app/views/sys_users.html b/server/app/views/sys_users.html new file mode 100644 index 00000000..5c145496 --- /dev/null +++ b/server/app/views/sys_users.html @@ -0,0 +1,34 @@ +
+ +
+
+ +
+ + +
+ + + + + + + + + + + + + + + + + +
RemoteLoginGravatarEmailCreatedUpdated
{{ user.remote }}{{ user.login }}{{ user.email }}{{ user.created_at | toDate}}{{ user.updated_at | toDate}}
+
+
\ No newline at end of file diff --git a/server/channel/channel.go b/server/channel/channel.go deleted file mode 100644 index 3c908090..00000000 --- a/server/channel/channel.go +++ /dev/null @@ -1,157 +0,0 @@ -package channel - -import ( - "crypto/rand" - "encoding/json" - "fmt" - "io" - "time" - - "code.google.com/p/go.net/websocket" - "github.com/dchest/authcookie" -) - -// secret key used to generate tokens -var secret = make([]byte, 32) - -func init() { - // generate the secret key by reading - // from crypto/random - if _, err := io.ReadFull(rand.Reader, secret); err != nil { - panic(err) - } -} - -// Create will generate a token and create a new -// channel over which messages will be sent. -func Create(name string) string { - mu.Lock() - defer mu.Unlock() - - if _, ok := hubs[name]; !ok { - hub := newHub(false, true) - hubs[name] = hub - go hub.run() - } - return authcookie.NewSinceNow(name, 24*time.Hour, secret) -} - -// CreateStream will generate a token and create a new -// channel over which messages streams (ie build output) -// are sent. -func CreateStream(name string) string { - mu.Lock() - defer mu.Unlock() - - if _, ok := hubs[name]; !ok { - hub := newHub(true, false) - hubs[name] = hub - go hub.run() - } - return authcookie.NewSinceNow(name, 24*time.Hour, secret) -} - -// Token will generate a token, but will not create -// a new channel. -func Token(name string) string { - return authcookie.NewSinceNow(name, 24*time.Hour, secret) -} - -// Send sends a message on the named channel. -func Send(name string, message string) error { - return SendBytes(name, []byte(message)) -} - -// SendJSON sends a JSON-encoded value on -// the named channel. -func SendJSON(name string, value interface{}) error { - m, err := json.Marshal(value) - if err != nil { - return err - } - - return SendBytes(name, m) -} - -// SendBytes send a message in byte format on -// the named channel. -func SendBytes(name string, value []byte) error { - // get the hub for the specified channel name - mu.RLock() - hub, ok := hubs[name] - mu.RUnlock() - - if !ok { - return fmt.Errorf("channel does not exist") - } - - go hub.Write(value) - return nil -} - -func Read(ws *websocket.Conn) { - - // get the name from the request - hash := ws.Request().FormValue("token") - - // get the hash of the token - name := authcookie.Login(hash, secret) - - // get the hub for the specified channel name - mu.RLock() - hub, ok := hubs[name] - mu.RUnlock() - - // if hub not found, exit - if !ok { - ws.Close() - return - } - - // internal representation of a connection - // maximum queue of 100000 messages - conn := &connection{ - send: make(chan string, 100000), - ws: ws, - } - - // register the connection with the hub - hub.register <- conn - - defer func() { - go func() { - hub.unregister <- conn - }() - closed := <-hub.closed - - // this will remove the hub when the connection is - // closed if the - if hub.autoClose && closed { - mu.Lock() - delete(hubs, name) - mu.Unlock() - } - }() - - go conn.writer() - conn.reader() -} - -func Close(name string) { - // get the hub for the specified channel name - mu.RLock() - hub, ok := hubs[name] - mu.RUnlock() - - if !ok { - return - } - - // close hub connections - hub.Close() - - // remove the hub - mu.Lock() - delete(hubs, name) - mu.Unlock() -} diff --git a/server/channel/conn.go b/server/channel/conn.go deleted file mode 100644 index d8c8fac5..00000000 --- a/server/channel/conn.go +++ /dev/null @@ -1,36 +0,0 @@ -package channel - -import ( - "code.google.com/p/go.net/websocket" -) - -type connection struct { - // The websocket connection. - ws *websocket.Conn - - // Buffered channel of outbound messages. - send chan string -} - -func (c *connection) reader() { - for { - var message string - err := websocket.Message.Receive(c.ws, &message) - if err != nil { - break - } - } - - c.ws.Close() -} - -func (c *connection) writer() { - for message := range c.send { - err := websocket.Message.Send(c.ws, message) - if err != nil { - break - } - } - - c.ws.Close() -} diff --git a/server/channel/hub.go b/server/channel/hub.go deleted file mode 100644 index 8cea6d43..00000000 --- a/server/channel/hub.go +++ /dev/null @@ -1,133 +0,0 @@ -package channel - -import ( - "sync" -) - -// mutex to lock access to the -// internal map of hubs. -var mu sync.RWMutex - -// a map of hubs. each hub represents a different -// channel that a set of users can listen on. For -// example, we may have a hub to stream build output -// for github.com/foo/bar or a channel to post -// updates for user octocat. -var hubs = map[string]*hub{} - -type hub struct { - // Registered connections - connections map[*connection]bool - - // Inbound messages from the connections. - broadcast chan string - - // Register requests from the connections. - register chan *connection - - // Unregister requests from connections. - unregister chan *connection - - // Buffer of sent data. This is used mostly - // for build output. A client may connect after - // the build has already started, in which case - // we need to stream them the build history. - history []string - - // Send a "shutdown" signal - close chan bool - - // Hub responds on this channel letting you know - // if it's active - closed chan bool - - // Auto shutdown when last connection removed - autoClose bool - - // Send history - sendHistory bool -} - -func newHub(sendHistory, autoClose bool) *hub { - h := hub{ - broadcast: make(chan string), - register: make(chan *connection), - unregister: make(chan *connection), - connections: make(map[*connection]bool), - history: make([]string, 0), // This should be pre-allocated, but it's not - close: make(chan bool), - autoClose: autoClose, - closed: make(chan bool), - sendHistory: sendHistory, - } - - return &h -} - -func sendHistory(c *connection, history []string) { - if len(history) > 0 { - for i := range history { - c.send <- history[i] - } - } -} - -func (h *hub) run() { - // make sure we don't bring down the application - // if somehow we encounter a nil pointer or some - // other unexpected behavior. - defer func() { - recover() - }() - - for { - select { - case c := <-h.register: - h.connections[c] = true - if len(h.history) > 0 { - b := make([]string, len(h.history)) - copy(b, h.history) - go sendHistory(c, b) - } - case c := <-h.unregister: - delete(h.connections, c) - close(c.send) - shutdown := h.autoClose && (len(h.connections) == 0) - if shutdown { - h.closed <- shutdown - return - } - h.closed <- shutdown - case m := <-h.broadcast: - if h.sendHistory { - h.history = append(h.history, m) - } - for c := range h.connections { - select { - case c.send <- m: - // do nothing - default: - delete(h.connections, c) - go c.ws.Close() - } - } - case <-h.close: - for c := range h.connections { - delete(h.connections, c) - close(c.send) - } - h.closed <- true - return - } - - } -} - -func (h *hub) Close() { - h.close <- true -} - -func (h *hub) Write(p []byte) (n int, err error) { - h.broadcast <- string(p) - return len(p), nil -} diff --git a/server/database/remote.go b/server/database/remote.go new file mode 100644 index 00000000..371b2a0c --- /dev/null +++ b/server/database/remote.go @@ -0,0 +1,89 @@ +package database + +import ( + "database/sql" + + "github.com/drone/drone/shared/model" + "github.com/russross/meddler" +) + +type RemoteManager interface { + // Find finds the Remote by ID. + Find(id int64) (*model.Remote, error) + + // FindHost finds the Remote by hostname. + FindHost(name string) (*model.Remote, error) + + // List finds all registered Remotes of the system. + List() ([]*model.Remote, error) + + // Insert persists the Remotes to the datastore. + Insert(server *model.Remote) error + + // Update persists changes to the Remotes to the datastore. + Update(server *model.Remote) error + + // Delete removes the Remotes from the datastore. + Delete(server *model.Remote) error +} + +// remoteManager manages a list of remotes in a SQL database. +type remoteManager struct { + *sql.DB +} + +// SQL query to retrieve a Remote by remote login. +const findRemoteQuery = ` +SELECT * +FROM remotes +WHERE remote_host=? +LIMIT 1 +` + +// SQL query to retrieve a list of all Remotes. +const listRemoteQuery = ` +SELECT * +FROM remotes +` + +// SQL statement to delete a Remote by ID. +const deleteRemoteStmt = ` +DELETE FROM remotes WHERE remote_id=? +` + +// NewRemoteManager initiales a new RemoteManager intended to +// manage and persist servers. +func NewRemoteManager(db *sql.DB) RemoteManager { + return &remoteManager{db} +} + +func (db *remoteManager) Find(id int64) (*model.Remote, error) { + dst := model.Remote{} + err := meddler.Load(db, "remotes", &dst, id) + return &dst, err +} + +func (db *remoteManager) FindHost(host string) (*model.Remote, error) { + dst := model.Remote{} + err := meddler.QueryRow(db, &dst, findRemoteQuery, host) + return &dst, err +} + +func (db *remoteManager) List() ([]*model.Remote, error) { + var dst []*model.Remote + err := meddler.QueryAll(db, &dst, listRemoteQuery) + return dst, err +} + +func (db *remoteManager) Insert(remote *model.Remote) error { + return meddler.Insert(db, "remotes", remote) +} + +func (db *remoteManager) Update(remote *model.Remote) error { + return meddler.Update(db, "remotes", remote) +} + +func (db *remoteManager) Delete(remote *model.Remote) error { + _, err := db.Exec(deleteRemoteStmt, remote.ID) + return err +} diff --git a/server/database/remote_test.go b/server/database/remote_test.go new file mode 100644 index 00000000..0ef7a824 --- /dev/null +++ b/server/database/remote_test.go @@ -0,0 +1,196 @@ +package database + +import ( + "database/sql" + "testing" + + "github.com/drone/drone/shared/model" +) + +func TestRemoteFind(t *testing.T) { + setup() + defer teardown() + + remotes := NewRemoteManager(db) + remote, err := remotes.Find(1) + if err != nil { + t.Errorf("Want Remote from ID, got %s", err) + } + + testRemote(t, remote) +} + +func TestRemoteFindHost(t *testing.T) { + setup() + defer teardown() + + remotes := NewRemoteManager(db) + remote, err := remotes.FindHost("github.drone.io") + if err != nil { + t.Errorf("Want Remote from Host, got %s", err) + } + + testRemote(t, remote) +} + +func TestRemoteList(t *testing.T) { + setup() + defer teardown() + + remotes := NewRemoteManager(db) + all, err := remotes.List() + if err != nil { + t.Errorf("Want Remotes, got %s", err) + } + + var got, want = len(all), 2 + if got != want { + t.Errorf("Want %v remotes, got %v", want, got) + } + + testRemote(t, all[0]) +} + +func TestRemoteInsert(t *testing.T) { + setup() + defer teardown() + + remote := &model.Remote{0, "bitbucket.org", "bitbucket.org", "https://bitbucket.org", "https://bitbucket.org", "abc", "123", false} + remotes := NewRemoteManager(db) + if err := remotes.Insert(remote); err != nil { + t.Errorf("Want Remote created, got %s", err) + } + + var got, want = remote.ID, int64(3) + if want != got { + t.Errorf("Want Remote ID %v, got %v", want, got) + } + + // verify unique remote name constraint + var err = remotes.Insert(&model.Remote{Type: "bitbucket.org", Host: "butbucket.com"}) + if err == nil { + t.Error("Want Type unique constraint violated") + } + +} + +func TestRemoteUpdate(t *testing.T) { + setup() + defer teardown() + + remotes := NewRemoteManager(db) + remote, err := remotes.Find(1) + if err != nil { + t.Errorf("Want Remote from ID, got %s", err) + } + + // update the remote's address + remote.Client = "abc" + remote.Secret = "123" + remote.Host = "git.drone.io" + remote.URL = "https://git.drone.io" + remote.API = "https://git.drone.io/v3/api" + if err := remotes.Update(remote); err != nil { + t.Errorf("Want Remote updated, got %s", err) + } + + updated, _ := remotes.Find(1) + var got, want = remote.Host, remote.Host + if got != want { + t.Errorf("Want updated Host %s, got %s", want, got) + } + + got, want = updated.Client, remote.Client + if got != want { + t.Errorf("Want updated Client %s, got %s", want, got) + } + + got, want = updated.Secret, remote.Secret + if got != want { + t.Errorf("Want updated Secret %s, got %s", want, got) + } + + got, want = updated.Host, remote.Host + if got != want { + t.Errorf("Want updated Host %s, got %s", want, got) + } + + got, want = updated.Host, remote.Host + if got != want { + t.Errorf("Want updated Host %s, got %s", want, got) + } + + got, want = updated.URL, remote.URL + if got != want { + t.Errorf("Want updated URL %s, got %s", want, got) + } + + got, want = updated.API, remote.API + if got != want { + t.Errorf("Want updated API %s, got %s", want, got) + } +} + +func TestRemoteDelete(t *testing.T) { + setup() + defer teardown() + + remotes := NewRemoteManager(db) + remote, err := remotes.Find(1) + if err != nil { + t.Errorf("Want Remote from ID, got %s", err) + } + + // delete the remote + if err := remotes.Delete(remote); err != nil { + t.Errorf("Want Remote deleted, got %s", err) + } + + // check to see if the deleted remote is actually gone + if _, err := remotes.Find(1); err != sql.ErrNoRows { + t.Errorf("Want ErrNoRows, got %s", err) + } +} + +func testRemote(t *testing.T, remote *model.Remote) { + + var got, want = remote.Host, "github.drone.io" + if got != want { + t.Errorf("Want Host %v, got %v", want, got) + } + + got, want = remote.Type, "enterprise.github.com" + if got != want { + t.Errorf("Want Type %v, got %v", want, got) + } + + got, want = remote.URL, "https://github.drone.io" + if got != want { + t.Errorf("Want URL %v, got %v", want, got) + } + + got, want = remote.API, "https://github.drone.io/v3/api" + if got != want { + t.Errorf("Want API %v, got %v", want, got) + } + + got, want = remote.Client, "f0b461ca586c27872b43a0685cbc2847" + if got != want { + t.Errorf("Want Access Token %v, got %v", want, got) + } + + got, want = remote.Secret, "976f22a5eef7caacb7e678d6c52f49b1" + if got != want { + t.Errorf("Want Token Secret %v, got %v", want, got) + } + + var gotBool, wantBool = remote.Open, true + if gotBool != wantBool { + t.Errorf("Want Open %v, got %v", wantBool, gotBool) + } + + var gotInt64, wantInt64 = remote.ID, int64(1) + if gotInt64 != wantInt64 { + t.Errorf("Want ID %v, got %v", wantInt64, gotInt64) + } +} diff --git a/server/database/schema/schema.go b/server/database/schema/schema.go index b7d303f7..a0a4d24c 100644 --- a/server/database/schema/schema.go +++ b/server/database/schema/schema.go @@ -85,6 +85,35 @@ var stmts = []string{` ,commit_id INTEGER ,output_raw BLOB ,UNIQUE(commit_id) + );`, ` + CREATE TABLE IF NOT EXISTS remotes ( + remote_id INTEGER PRIMARY KEY AUTOINCREMENT + ,remote_type VARCHAR(255) + ,remote_host VARCHAR(255) + ,remote_url VARCHAR(255) + ,remote_api VARCHAR(255) + ,remote_client VARCHAR(255) + ,remote_secret VARCHAR(255) + ,remote_open BOOLEAN + ,UNIQUE(remote_host) + ,UNIQUE(remote_type) + );`, ` + CREATE TABLE IF NOT EXISTS servers ( + server_id INTEGER PRIMARY KEY AUTOINCREMENT + ,server_name VARCHAR(255) + ,server_host VARCHAR(255) + ,server_user VARCHAR(255) + ,server_pass VARCHAR(255) + ,server_cert VARCHAR(4000) + ,UNIQUE(server_name) + );`, ` + CREATE TABLE IF NOT EXISTS smtp ( + smtp_id INTEGER PRIMARY KEY AUTOINCREMENT + ,smtp_from VARCHAR(255) + ,smtp_host VARCHAR(255) + ,smtp_port VARCHAR(255) + ,smtp_user VARCHAR(255) + ,smtp_pass VARCHAR(255) );`, } diff --git a/server/database/server.go b/server/database/server.go new file mode 100644 index 00000000..886ce407 --- /dev/null +++ b/server/database/server.go @@ -0,0 +1,111 @@ +package database + +import ( + "database/sql" + + "github.com/drone/drone/shared/model" + "github.com/russross/meddler" +) + +type ServerManager interface { + // Find finds the Server by ID. + Find(id int64) (*model.Server, error) + + // FindName finds the Server by name. + FindName(name string) (*model.Server, error) + + // FindName finds the Server by name. + FindSMTP() (*model.SMTPServer, error) + + // List finds all registered Servers of the system. + List() ([]*model.Server, error) + + // Insert persists the Server to the datastore. + Insert(server *model.Server) error + + // Update persists changes to the Server to the datastore. + Update(server *model.Server) error + + // UpdateSMTP persists changes to the SMTP Server to the datastore. + UpdateSMTP(server *model.SMTPServer) error + + // Delete removes the Server from the datastore. + Delete(server *model.Server) error +} + +// serverManager manages a list of users in a SQL database. +type serverManager struct { + *sql.DB +} + +// SQL query to retrieve a Server by remote login. +const findServerQuery = ` +SELECT * +FROM servers +WHERE server_name=? +LIMIT 1 +` + +// SQL query to retrieve a list of all Servers. +const listServerQuery = ` +SELECT * +FROM servers +` + +// SQL statement to delete a Server by ID. +const deleteServerStmt = ` +DELETE FROM servers WHERE server_id=? +` + +// NewServerManager initiales a new ServerManager intended to +// manage and persist servers. +func NewServerManager(db *sql.DB) ServerManager { + return &serverManager{db} +} + +func (db *serverManager) Find(id int64) (*model.Server, error) { + dst := model.Server{} + err := meddler.Load(db, "servers", &dst, id) + return &dst, err +} + +func (db *serverManager) FindName(name string) (*model.Server, error) { + dst := model.Server{} + err := meddler.QueryRow(db, &dst, findServerQuery, name) + return &dst, err +} + +func (db *serverManager) FindSMTP() (*model.SMTPServer, error) { + dst := model.SMTPServer{} + err := meddler.Load(db, "smtp", &dst, 1) + if err != nil && err != sql.ErrNoRows { + return &dst, err + } + return &dst, nil +} + +func (db *serverManager) List() ([]*model.Server, error) { + var dst []*model.Server + err := meddler.QueryAll(db, &dst, listServerQuery) + return dst, err +} + +func (db *serverManager) Insert(server *model.Server) error { + return meddler.Insert(db, "servers", server) +} + +func (db *serverManager) Update(server *model.Server) error { + return meddler.Update(db, "servers", server) +} + +func (db *serverManager) UpdateSMTP(server *model.SMTPServer) error { + server.ID = 0 + meddler.Insert(db, "smtp", server) + server.ID = 1 + return meddler.Update(db, "smtp", server) +} + +func (db *serverManager) Delete(server *model.Server) error { + _, err := db.Exec(deleteServerStmt, server.ID) + return err +} diff --git a/server/database/server_test.go b/server/database/server_test.go new file mode 100644 index 00000000..2cf513c1 --- /dev/null +++ b/server/database/server_test.go @@ -0,0 +1,201 @@ +package database + +import ( + "database/sql" + "testing" + + "github.com/drone/drone/shared/model" +) + +func TestServerFind(t *testing.T) { + setup() + defer teardown() + + servers := NewServerManager(db) + server, err := servers.Find(1) + if err != nil { + t.Errorf("Want Server from ID, got %s", err) + } + + testServer(t, server) +} + +func TestServerFindName(t *testing.T) { + setup() + defer teardown() + + servers := NewServerManager(db) + server, err := servers.FindName("docker1") + if err != nil { + t.Errorf("Want Server from Host, got %s", err) + } + + testServer(t, server) +} + +func TestServerFindSMTP(t *testing.T) { + setup() + defer teardown() + server := model.SMTPServer{ + From: "foo@bar.com", + Host: "127.0.0.1", + User: "foo"} + + servers := NewServerManager(db) + if err := servers.UpdateSMTP(&server); err != nil { + t.Errorf("Want SMTP server inserted, got %s", err) + } + if inserted, err := servers.FindSMTP(); err != nil { + t.Errorf("Want SMTP server, got %s", err) + } else if inserted.ID == 0 { + t.Errorf("Want SMTP server inserted") + } + + server.Host = "0.0.0.0" + server.User = "bar" + err := servers.UpdateSMTP(&server) + if err := servers.UpdateSMTP(&server); err != nil { + t.Errorf("Want SMTP server updated, got %s", err) + } + + updated, err := servers.FindSMTP() + if err != nil { + t.Errorf("Want SMTP server, got %s", err) + } + + var want, got = server.Host, updated.Host + if want != got { + t.Errorf("Want SMTP Host %v, got %v", want, got) + } +} + +func TestServerList(t *testing.T) { + setup() + defer teardown() + + servers := NewServerManager(db) + all, err := servers.List() + if err != nil { + t.Errorf("Want Servers, got %s", err) + } + + var got, want = len(all), 2 + if got != want { + t.Errorf("Want %v Servers, got %v", want, got) + } + + testServer(t, all[0]) +} + +func TestServerInsert(t *testing.T) { + setup() + defer teardown() + + server := &model.Server{Host: "tcp://127.0.0.1:4243", Name: "docker3"} + servers := NewServerManager(db) + if err := servers.Insert(server); err != nil { + t.Errorf("Want Server created, got %s", err) + } + + var got, want = server.ID, int64(3) + if want != got { + t.Errorf("Want Server ID %v, got %v", want, got) + } + + // verify unique server name constraint + var err = servers.Insert(&model.Server{Host: "tcp://127.0.0.1:4243", Name: "docker3"}) + if err == nil { + t.Error("Want Name unique constraint violated") + } +} + +func TestServerUpdate(t *testing.T) { + setup() + defer teardown() + + servers := NewServerManager(db) + server, err := servers.Find(1) + if err != nil { + t.Errorf("Want Server from ID, got %s", err) + } + + // update the server's address + server.Host = "tcp://1.2.3.4:4243" + server.User = "docker" + server.Pass = "123456" + if err := servers.Update(server); err != nil { + t.Errorf("Want Server updated, got %s", err) + } + + updated, _ := servers.Find(1) + var got, want = server.Host, server.Host + if got != want { + t.Errorf("Want updated Host %s, got %s", want, got) + } + + got, want = updated.User, server.User + if got != want { + t.Errorf("Want updated User %s, got %s", want, got) + } + + got, want = updated.Pass, server.Pass + if got != want { + t.Errorf("Want updated Pass %s, got %s", want, got) + } +} + +func TestServerDelete(t *testing.T) { + setup() + defer teardown() + + servers := NewServerManager(db) + server, err := servers.Find(1) + if err != nil { + t.Errorf("Want Server from ID, got %s", err) + } + + // delete the server + if err := servers.Delete(server); err != nil { + t.Errorf("Want Server deleted, got %s", err) + } + + // check to see if the deleted server is actually gone + if _, err := servers.Find(1); err != sql.ErrNoRows { + t.Errorf("Want ErrNoRows, got %s", err) + } +} + +// testServer is a helper function that compares the server +// to an expected set of fixed field values. +func testServer(t *testing.T, server *model.Server) { + + var got, want = server.Host, "tcp://127.0.0.1:4243" + if got != want { + t.Errorf("Want Host %v, got %v", want, got) + } + + got, want = server.Name, "docker1" + if got != want { + t.Errorf("Want Name %v, got %v", want, got) + } + + got, want = server.User, "root" + if got != want { + t.Errorf("Want User %v, got %v", want, got) + } + + got, want = server.Pass, "pa55word" + if got != want { + t.Errorf("Want Pass %v, got %v", want, got) + } + + got, want = server.Cert, "/path/to/cert.key" + if got != want { + t.Errorf("Want Cert %v, got %v", want, got) + } + + var gotInt64, wantInt64 = server.ID, int64(1) + if gotInt64 != wantInt64 { + t.Errorf("Want ID %v, got %v", wantInt64, gotInt64) + } +} diff --git a/server/database/testdata/testdata.go b/server/database/testdata/testdata.go index 8e6bcd26..929a7811 100644 --- a/server/database/testdata/testdata.go +++ b/server/database/testdata/testdata.go @@ -34,6 +34,14 @@ var stmts = []string{ // insert commit console output "insert into output values (1, 1, 'sample console output');", "insert into output values (2, 2, 'sample console output.....');", + + // insert server entries + "insert into servers values (1, 'docker1', 'tcp://127.0.0.1:4243', 'root', 'pa55word', '/path/to/cert.key');", + "insert into servers values (2, 'docker2', 'tcp://127.0.0.1:4243', 'root', 'pa55word', '/path/to/cert.key');", + + // insert remote entries + "insert into remotes values (1, 'enterprise.github.com', 'github.drone.io', 'https://github.drone.io', 'https://github.drone.io/v3/api', 'f0b461ca586c27872b43a0685cbc2847', '976f22a5eef7caacb7e678d6c52f49b1', '1');", + "insert into remotes values (2, 'github.com', 'github.com', 'https://github.io', 'https://api.github.com', 'a0b461ca586c27872b43a0685cbc2847', 'a76f22a5eef7caacb7e678d6c52f49b1', '0');", } // Load will populate the database with a fixed dataset for diff --git a/server/database/user.go b/server/database/user.go index 7268c007..11e4858e 100644 --- a/server/database/user.go +++ b/server/database/user.go @@ -29,6 +29,9 @@ type UserManager interface { // Delete removes the User from the datastore. Delete(user *model.User) error + + // Exist returns true if Users exist in the system. + Exist() bool } // userManager manages a list of users in a SQL database. @@ -65,6 +68,11 @@ const deleteUserStmt = ` DELETE FROM users WHERE user_id=? ` +// SQL statement to check if users exist. +const confirmUserStmt = ` +select 0 from users limit 1 +` + // NewUserManager initiales a new UserManager intended to // manage and persist commits. func NewUserManager(db *sql.DB) UserManager { @@ -110,3 +118,10 @@ func (db *userManager) Delete(user *model.User) error { _, err := db.Exec(deleteUserStmt, user.ID) return err } + +func (db *userManager) Exist() bool { + row := db.QueryRow(confirmUserStmt) + var result int + row.Scan(&result) + return result == 1 +} diff --git a/server/handler/commit.go b/server/handler/commit.go index d47fc156..d5fb2f61 100644 --- a/server/handler/commit.go +++ b/server/handler/commit.go @@ -5,8 +5,8 @@ import ( "net/http" "github.com/drone/drone/server/database" - "github.com/drone/drone/server/queue" "github.com/drone/drone/server/session" + "github.com/drone/drone/server/worker" "github.com/drone/drone/shared/model" "github.com/gorilla/pat" ) @@ -16,10 +16,10 @@ type CommitHandler struct { repos database.RepoManager commits database.CommitManager sess session.Session - queue *queue.Queue + queue chan *worker.Request } -func NewCommitHandler(repos database.RepoManager, commits database.CommitManager, perms database.PermManager, sess session.Session, queue *queue.Queue) *CommitHandler { +func NewCommitHandler(repos database.RepoManager, commits database.CommitManager, perms database.PermManager, sess session.Session, queue chan *worker.Request) *CommitHandler { return &CommitHandler{perms, repos, commits, sess, queue} } @@ -158,7 +158,13 @@ func (h *CommitHandler) PostCommit(w http.ResponseWriter, r *http.Request) error } // drop the items on the queue - h.queue.Add(&queue.BuildTask{Repo: repo, Commit: c}) + // drop the items on the queue + go func() { + h.queue <- &worker.Request{ + Repo: repo, + Commit: c, + } + }() return nil } diff --git a/server/handler/hook.go b/server/handler/hook.go index a73cd265..20ef5898 100644 --- a/server/handler/hook.go +++ b/server/handler/hook.go @@ -4,7 +4,7 @@ import ( "net/http" "github.com/drone/drone/server/database" - "github.com/drone/drone/server/queue" + "github.com/drone/drone/server/worker" "github.com/drone/drone/shared/model" "github.com/gorilla/pat" ) @@ -14,10 +14,10 @@ type HookHandler struct { repos database.RepoManager commits database.CommitManager conf database.ConfigManager - queue *queue.Queue + queue chan *worker.Request } -func NewHookHandler(users database.UserManager, repos database.RepoManager, commits database.CommitManager, conf database.ConfigManager, queue *queue.Queue) *HookHandler { +func NewHookHandler(users database.UserManager, repos database.RepoManager, commits database.CommitManager, conf database.ConfigManager, queue chan *worker.Request) *HookHandler { return &HookHandler{users, repos, commits, conf, queue} } @@ -92,11 +92,16 @@ func (h *HookHandler) PostHook(w http.ResponseWriter, r *http.Request) error { //fmt.Printf("%s", yml) // drop the items on the queue - h.queue.Add(&queue.BuildTask{Repo: repo, Commit: &c}) + go func() { + h.queue <- &worker.Request{ + Repo: repo, + Commit: &c, + } + }() return nil } func (h *HookHandler) Register(r *pat.Router) { - r.Post("/hook/{host}", errorHandler(h.PostHook)) - r.Put("/hook/{host}", errorHandler(h.PostHook)) + r.Post("/v1/hook/{host}", errorHandler(h.PostHook)) + r.Put("/v1/hook/{host}", errorHandler(h.PostHook)) } diff --git a/server/handler/login.go b/server/handler/login.go index 50783fa8..1f228549 100644 --- a/server/handler/login.go +++ b/server/handler/login.go @@ -48,8 +48,9 @@ func (h *LoginHandler) GetLogin(w http.ResponseWriter, r *http.Request) error { u, err := h.users.FindLogin(host, login.Login) if err != nil { // if self-registration is disabled we should - // return a notAuthorized error - if !h.conf.Find().Registration { + // return a notAuthorized error. the only exception + // is if no users exist yet in the system we'll proceed. + if h.conf.Find().Registration == false && h.users.Exist() { return notAuthorized{} } diff --git a/server/handler/remote.go b/server/handler/remote.go new file mode 100644 index 00000000..bc4b664d --- /dev/null +++ b/server/handler/remote.go @@ -0,0 +1,108 @@ +package handler + +import ( + "encoding/json" + "net/http" + "net/url" + + "github.com/drone/drone/server/database" + "github.com/drone/drone/server/session" + "github.com/drone/drone/shared/model" + "github.com/gorilla/pat" +) + +type RemoteHandler struct { + users database.UserManager + remotes database.RemoteManager + sess session.Session +} + +func NewRemoteHandler(users database.UserManager, remotes database.RemoteManager, sess session.Session) *RemoteHandler { + return &RemoteHandler{users, remotes, sess} +} + +// GetRemotes gets all remotes. +// GET /api/remotes +func (h *RemoteHandler) GetRemotes(w http.ResponseWriter, r *http.Request) error { + // get the user form the session + user := h.sess.User(r) + if user == nil || !user.Admin { + return notAuthorized{} + } + // get all remotes + remotes, err := h.remotes.List() + if err != nil { + return internalServerError{err} + } + + return json.NewEncoder(w).Encode(remotes) +} + +// PostRemote creates a new remote. +// POST /api/remotes +func (h *RemoteHandler) PostRemote(w http.ResponseWriter, r *http.Request) error { + // get the user form the session + user := h.sess.User(r) + if user == nil || !user.Admin { + // if no users exist, this request is part of + // the system installation process and can proceed. + // else we should reject. + if h.users.Exist() { + return notAuthorized{} + } + } + // unmarshal the remote from the payload + defer r.Body.Close() + in := model.Remote{} + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + return badRequest{err} + } + + uri, err := url.Parse(in.URL) + if err != nil { + return badRequest{err} + } + in.Host = uri.Host + + // there is an edge case where, during installation, a user could attempt + // to add the same result multiple times. + if remote, err := h.remotes.FindHost(in.Host); err == nil && h.users.Exist() { + h.remotes.Delete(remote) + } + + // insert the remote in the database + if err := h.remotes.Insert(&in); err != nil { + return internalServerError{err} + } + + return json.NewEncoder(w).Encode(&in) +} + +// DeleteRemote delete the remote. +// GET /api/remotes/:name +func (h *RemoteHandler) DeleteRemote(w http.ResponseWriter, r *http.Request) error { + host := r.FormValue(":host") + + // get the user form the session + user := h.sess.User(r) + if user == nil || !user.Admin { + return notAuthorized{} + } + // get the remote + remote, err := h.remotes.FindHost(host) + if err != nil { + return notFound{err} + } + if err := h.remotes.Delete(remote); err != nil { + return internalServerError{err} + } + + w.WriteHeader(http.StatusNoContent) + return nil +} + +func (h *RemoteHandler) Register(r *pat.Router) { + r.Delete("/v1/remotes/:name", errorHandler(h.DeleteRemote)) + r.Post("/v1/remotes", errorHandler(h.PostRemote)) + r.Get("/v1/remotes", errorHandler(h.GetRemotes)) +} diff --git a/server/handler/repo.go b/server/handler/repo.go index 8a0b3b5c..ddbb84e0 100644 --- a/server/handler/repo.go +++ b/server/handler/repo.go @@ -92,7 +92,7 @@ func (h *RepoHandler) PostRepo(w http.ResponseWriter, r *http.Request) error { } // post commit hook url - hook := fmt.Sprintf("%s://%s/hook/%s", httputil.GetScheme(r), httputil.GetHost(r), remote.GetName()) + hook := fmt.Sprintf("%s://%s/v1/hook/%s", httputil.GetScheme(r), httputil.GetHost(r), remote.GetName()) // activate the repository in the remote system client := remote.GetClient(user.Access, user.Secret) diff --git a/server/handler/server.go b/server/handler/server.go new file mode 100644 index 00000000..d1aae8fa --- /dev/null +++ b/server/handler/server.go @@ -0,0 +1,88 @@ +package handler + +import ( + "encoding/json" + "net/http" + + "github.com/drone/drone/server/database" + "github.com/drone/drone/server/session" + "github.com/drone/drone/shared/model" + "github.com/gorilla/pat" +) + +type ServerHandler struct { + servers database.ServerManager + sess session.Session +} + +func NewServerHandler(servers database.ServerManager, sess session.Session) *ServerHandler { + return &ServerHandler{servers, sess} +} + +// GetServers gets all servers. +// GET /api/servers +func (h *ServerHandler) GetServers(w http.ResponseWriter, r *http.Request) error { + // get the user form the session + user := h.sess.User(r) + if user == nil || !user.Admin { + return notAuthorized{} + } + // get all servers + servers, err := h.servers.List() + if err != nil { + return internalServerError{err} + } + + return json.NewEncoder(w).Encode(servers) +} + +// PostServer creates a new server. +// POST /api/servers +func (h *ServerHandler) PostServer(w http.ResponseWriter, r *http.Request) error { + // get the user form the session + user := h.sess.User(r) + if user == nil || !user.Admin { + return notAuthorized{} + } + // unmarshal the server from the payload + defer r.Body.Close() + in := model.Server{} + if err := json.NewDecoder(r.Body).Decode(&in); err != nil { + return badRequest{err} + } + // insert the server in the database + if err := h.servers.Insert(&in); err != nil { + return internalServerError{err} + } + + return json.NewEncoder(w).Encode(&in) +} + +// DeleteServers deletes the named server. +// GET /api/servers/:name +func (h *ServerHandler) DeleteServer(w http.ResponseWriter, r *http.Request) error { + name := r.FormValue(":name") + + // get the user form the session + user := h.sess.User(r) + if user == nil || !user.Admin { + return notAuthorized{} + } + // get the server + server, err := h.servers.FindName(name) + if err != nil { + return notFound{err} + } + if err := h.servers.Delete(server); err != nil { + return internalServerError{err} + } + + w.WriteHeader(http.StatusNoContent) + return nil +} + +func (h *ServerHandler) Register(r *pat.Router) { + r.Delete("/v1/servers/:name", errorHandler(h.DeleteServer)) + r.Post("/v1/servers", errorHandler(h.PostServer)) + r.Get("/v1/servers", errorHandler(h.GetServers)) +} diff --git a/server/handler/site.go b/server/handler/site.go deleted file mode 100644 index b670d307..00000000 --- a/server/handler/site.go +++ /dev/null @@ -1,186 +0,0 @@ -package handler - -import ( - "io" - "net/http" - - "github.com/drone/drone/server/channel" - "github.com/drone/drone/server/database" - "github.com/drone/drone/server/session" - "github.com/drone/drone/shared/httputil" - "github.com/drone/drone/shared/model" - "github.com/gorilla/pat" -) - -type Renderer func(wr io.Writer, name string, data interface{}) error - -type SiteHandler struct { - users database.UserManager - repos database.RepoManager - commits database.CommitManager - perms database.PermManager - sess session.Session - render Renderer -} - -func NewSiteHandler(users database.UserManager, repos database.RepoManager, commits database.CommitManager, perms database.PermManager, sess session.Session, render Renderer) *SiteHandler { - return &SiteHandler{users, repos, commits, perms, sess, render} -} - -// GetIndex serves the root domain request. This is forwarded to the dashboard -// page iff the user is authenticated, else it is forwarded to the login page. -func (s *SiteHandler) GetIndex(w http.ResponseWriter, r *http.Request) error { - u := s.sess.User(r) - if u == nil { - http.Redirect(w, r, "/login", http.StatusSeeOther) - return nil - } - feed, _ := s.commits.ListUser(u.ID) - data := struct { - User *model.User - Feed []*model.CommitRepo - }{u, feed} - return s.render(w, "user_feed.html", &data) -} - -// GetLogin serves the account login page -func (s *SiteHandler) GetLogin(w http.ResponseWriter, r *http.Request) error { - return s.render(w, "login.html", struct{ User *model.User }{nil}) -} - -// GetUser serves the account settings page. -func (s *SiteHandler) GetUser(w http.ResponseWriter, r *http.Request) error { - u := s.sess.User(r) - if u == nil { - return s.render(w, "404.html", nil) - } - return s.render(w, "user_conf.html", struct{ User *model.User }{u}) -} - -func (s *SiteHandler) GetUsers(w http.ResponseWriter, r *http.Request) error { - u := s.sess.User(r) - if u == nil || u.Admin == false { - return s.render(w, "404.html", nil) - } - return s.render(w, "admin_users.html", struct{ User *model.User }{u}) -} - -func (s *SiteHandler) GetConfig(w http.ResponseWriter, r *http.Request) error { - u := s.sess.User(r) - if u == nil || u.Admin == false { - return s.render(w, "404.html", nil) - } - return s.render(w, "admin_conf.html", struct{ User *model.User }{u}) -} - -func (s *SiteHandler) GetRepo(w http.ResponseWriter, r *http.Request) error { - host, owner, name := parseRepo(r) - branch := parseBranch(r) - sha := parseCommit(r) - usr := s.sess.User(r) - - arepo, err := s.repos.FindName(host, owner, name) - if err != nil { - return s.render(w, "404.html", nil) - } - if ok, _ := s.perms.Read(usr, arepo); !ok { - return s.render(w, "404.html", nil) - } - data := struct { - User *model.User - Repo *model.Repo - Branch string - Channel string - Stream string - Branches []*model.Commit - Commits []*model.Commit - Commit *model.Commit - }{User: usr, Repo: arepo} - - // generate a token for connecting to the streaming server - // to get notified of feed items. - data.Channel = channel.Create(host + "/" + owner + "/" + name + "/") - - // if commit details are provided we should retrieve the build details - // and serve the build page. - if len(sha) != 0 { - data.Commit, err = s.commits.FindSha(data.Repo.ID, branch, sha) - if err != nil { - return s.render(w, "404.html", nil) - } - - // generate a token for connecting to the streaming server - // to get notified of feed items. - data.Stream = channel.Create(host + "/" + owner + "/" + name + "/" + branch + "/" + sha) - - return s.render(w, "repo_commit.html", &data) - } - - // retrieve the list of recently built branches - data.Branches, _ = s.commits.ListBranches(data.Repo.ID) - - // if the branch parameter is provided we should retrieve the build - // feed for this branch only. - if len(branch) != 0 { - data.Branch = branch - data.Commits, err = s.commits.ListBranch(data.Repo.ID, branch) - if err != nil { - return s.render(w, "404.html", nil) - } - return s.render(w, "repo_branch.html", &data) - } - - // else we should serve the standard build feed - data.Commits, _ = s.commits.List(data.Repo.ID) - return s.render(w, "repo_feed.html", &data) -} - -func (s *SiteHandler) GetRepoAdmin(w http.ResponseWriter, r *http.Request) error { - host, owner, name := parseRepo(r) - arepo, err := s.repos.FindName(host, owner, name) - u := s.sess.User(r) - if err != nil { - return s.render(w, "404.html", nil) - } - if ok, _ := s.perms.Admin(u, arepo); !ok { - return s.render(w, "404.html", nil) - } - data := struct { - User *model.User - Repo *model.Repo - Host string - Scheme string - }{u, arepo, httputil.GetHost(r), httputil.GetScheme(r)} - return s.render(w, "repo_conf.html", &data) -} - -// GetRepos serves a page that lists all user repositories. -func (s *SiteHandler) GetRepos(w http.ResponseWriter, r *http.Request) error { - u := s.sess.User(r) - if u == nil || u.Admin == false { - return s.render(w, "404.html", nil) - } - repos, err := s.repos.List(u.ID) - if err != nil { - s.render(w, "500.html", nil) - } - data := struct { - User *model.User - Repos []*model.Repo - }{u, repos} - return s.render(w, "user_repos.html", &data) -} - -func (s *SiteHandler) Register(r *pat.Router) { - - r.Get("/admin/users", errorHandler(s.GetUsers)) - r.Get("/admin/settings", errorHandler(s.GetConfig)) - r.Get("/account/profile", errorHandler(s.GetUser)) - r.Get("/account/repos", errorHandler(s.GetRepos)) - r.Get("/login", errorHandler(s.GetLogin)) - r.Get("/{host}/{owner}/{name}/settings", errorHandler(s.GetRepoAdmin)) - r.Get("/{host}/{owner}/{name}/branch/{branch}/commit/{commit}", errorHandler(s.GetRepo)) - r.Get("/{host}/{owner}/{name}/branch/{branch}", errorHandler(s.GetRepo)) - r.Get("/{host}/{owner}/{name}", errorHandler(s.GetRepo)) - r.Get("/", errorHandler(s.GetIndex)) -} diff --git a/server/handler/ws.go b/server/handler/ws.go new file mode 100644 index 00000000..3c018ae2 --- /dev/null +++ b/server/handler/ws.go @@ -0,0 +1,216 @@ +package handler + +import ( + "net/http" + "time" + + "github.com/drone/drone/server/database" + "github.com/drone/drone/server/pubsub" + "github.com/drone/drone/server/session" + "github.com/drone/drone/server/worker" + "github.com/drone/drone/shared/model" + "github.com/gorilla/pat" + + "github.com/gorilla/websocket" +) + +const ( + // Time allowed to write the message to the client. + writeWait = 10 * 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 = (pongWait * 9) / 10 +) + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +type WsHandler struct { + pubsub *pubsub.PubSub + commits database.CommitManager + perms database.PermManager + repos database.RepoManager + sess session.Session +} + +func NewWsHandler(repos database.RepoManager, commits database.CommitManager, perms database.PermManager, sess session.Session, pubsub *pubsub.PubSub) *WsHandler { + return &WsHandler{pubsub, commits, perms, repos, sess} +} + +// WsUser will upgrade the connection to a Websocket and will stream +// all events to the browser pertinent to the authenticated user. If the user +// is not authenticated, only public events are streamed. +func (h *WsHandler) WsUser(w http.ResponseWriter, r *http.Request) error { + // get the user form the session + user := h.sess.UserCookie(r) + + // upgrade the websocket + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return badRequest{err} + } + + // register a channel for global events + channel := h.pubsub.Register("_global") + sub := channel.Subscribe() + + ticker := time.NewTicker(pingPeriod) + defer func() { + ticker.Stop() + sub.Close() + ws.Close() + }() + + go func() { + for { + select { + case msg := <-sub.Read(): + work, ok := msg.(*worker.Request) + if !ok { + break + } + + // user must have read access to the repository + // in order to pass this message along + if ok, _ := h.perms.Read(user, work.Repo); !ok { + break + } + + ws.SetWriteDeadline(time.Now().Add(writeWait)) + err := ws.WriteJSON(work) + if err != nil { + ws.Close() + return + } + case <-sub.CloseNotify(): + ws.Close() + return + case <-ticker.C: + ws.SetWriteDeadline(time.Now().Add(writeWait)) + err := ws.WriteMessage(websocket.PingMessage, []byte{}) + if err != nil { + ws.Close() + return + } + } + } + }() + + readWebsocket(ws) + return nil + +} + +// WsConsole will upgrade the connection to a Websocket and will stream +// the build output to the browser. +func (h *WsHandler) WsConsole(w http.ResponseWriter, r *http.Request) error { + var host, owner, name = parseRepo(r) + var branch = r.FormValue(":branch") + var sha = r.FormValue(":commit") + + repo, err := h.repos.FindName(host, owner, name) + if err != nil { + return notFound{err} + } + commit, err := h.commits.FindSha(repo.ID, branch, sha) + if err != nil { + return notFound{err} + } + user := h.sess.UserCookie(r) + if ok, _ := h.perms.Read(user, repo); !ok { + return notFound{err} + } + + // find a channel that we can subscribe to + // and listen for stream updates. + channel := h.pubsub.Lookup(commit.ID) + if channel == nil { + return notFound{} + } + sub := channel.Subscribe() + defer sub.Close() + + // upgrade the websocket + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return badRequest{err} + } + + ticker := time.NewTicker(pingPeriod) + defer func() { + ticker.Stop() + ws.Close() + }() + + go func() { + for { + select { + case msg := <-sub.Read(): + data, ok := msg.([]byte) + if !ok { + break + } + ws.SetWriteDeadline(time.Now().Add(writeWait)) + err := ws.WriteMessage(websocket.TextMessage, data) + if err != nil { + ws.Close() + return + } + case <-sub.CloseNotify(): + ws.Close() + return + case <-ticker.C: + ws.SetWriteDeadline(time.Now().Add(writeWait)) + err := ws.WriteMessage(websocket.PingMessage, []byte{}) + if err != nil { + ws.Close() + return + } + } + } + }() + + readWebsocket(ws) + return nil +} + +// readWebsocket will block while reading the websocket data +func readWebsocket(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 + } + } +} + +// Ping is a method that is being used for internal testing and +// will be removed prior to release +func (h *WsHandler) Ping(w http.ResponseWriter, r *http.Request) error { + channel := h.pubsub.Register("_global") + msg := worker.Request{ + Repo: &model.Repo{ID: 1, Private: false, Host: "github.com", Owner: "ping", Name: "pong"}, + Commit: &model.Commit{ID: 1, Sha: "000000000000000000000", Message: "hello world"}, + } + channel.Publish(&msg) + w.WriteHeader(http.StatusOK) + return nil +} + +func (h *WsHandler) Register(r *pat.Router) { + r.Post("/ws/ping", errorHandler(h.Ping)) + r.Get("/ws/user", errorHandler(h.WsUser)) + r.Get("/ws/{host}/{owner}/{name}/branches/{branch}/commits/{commit}", errorHandler(h.WsConsole)) +} diff --git a/server/main.go b/server/main.go index 6f09e340..d708ce92 100644 --- a/server/main.go +++ b/server/main.go @@ -3,24 +3,22 @@ package main import ( "database/sql" "flag" - "html/template" "io/ioutil" "net/http" "os" "os/user" "path/filepath" "runtime" - "time" + "strings" - "code.google.com/p/go.net/websocket" - "github.com/drone/drone/server/channel" "github.com/drone/drone/server/database" "github.com/drone/drone/server/database/schema" "github.com/drone/drone/server/handler" - "github.com/drone/drone/server/queue" + "github.com/drone/drone/server/pubsub" "github.com/drone/drone/server/session" - "github.com/drone/drone/shared/build/docker" + "github.com/drone/drone/server/worker" "github.com/drone/drone/shared/build/log" + "github.com/drone/drone/shared/model" "github.com/gorilla/pat" //"github.com/justinas/nosurf" @@ -51,10 +49,6 @@ var ( version string = "0.2-dev" revision string - // build will timeout after N milliseconds. - // this will default to 500 minutes (6 hours) - timeout time.Duration - // Number of concurrent build workers to run // default to number of CPUs on machine workers int @@ -70,25 +64,9 @@ func main() { flag.StringVar(&datasource, "datasource", "drone.sqlite", "") flag.StringVar(&sslcert, "sslcert", "", "") flag.StringVar(&sslkey, "sslkey", "", "") - flag.DurationVar(&timeout, "timeout", 300*time.Minute, "") flag.IntVar(&workers, "workers", runtime.NumCPU(), "") flag.Parse() - // parse the template files - // TODO we need to retrieve these from go.rice - //templ := template.Must( - // template.New("_").Funcs(render.FuncMap).ParseGlob("template/html/*.html"), - //).ExecuteTemplate - - // load the html templates - templateBox := rice.MustFindBox("template/html") - templateFiles := []string{"login.html", "repo_branch.html", "repo_commit.html", "repo_conf.html", "repo_feed.html", "user_conf.html", "user_feed.html", "user_login.html", "user_repos.html", "404.html", "400.html"} - templ := template.New("_").Funcs(funcMap) - for _, file := range templateFiles { - templateData, _ := templateBox.String(file) - templ, _ = templ.New(file).Parse(templateData) - } - // setup the database meddler.Default = meddler.SQLite db, _ := sql.Open(driver, datasource) @@ -99,14 +77,24 @@ func main() { users := database.NewUserManager(db) perms := database.NewPermManager(db) commits := database.NewCommitManager(db) + servers := database.NewServerManager(db) + remotes := database.NewRemoteManager(db) configs := database.NewConfigManager(filepath.Join(home, "config.toml")) + // message broker + pubsub := pubsub.NewPubSub() + // cancel all previously running builds go commits.CancelAll() // setup the build queue - queueRunner := queue.NewBuildRunner(docker.New(), timeout) - queue := queue.Start(workers, commits, queueRunner) + //queueRunner := queue.NewBuildRunner(docker.New(), timeout) + //queue := queue.Start(work ers, commits, queueRunner) + + queue := make(chan *worker.Request) + workers := make(chan chan *worker.Request) + worker.NewDispatch(queue, workers).Start() + worker.NewWorker(workers, users, repos, commits, configs, pubsub, &model.Server{Host: "unix:///tmp/sock.sock"}).Start() // setup the session managers sess := session.NewSession(users) @@ -122,28 +110,51 @@ func main() { handler.NewRepoHandler(repos, commits, perms, sess, configs).Register(router) handler.NewBadgeHandler(repos, commits).Register(router) handler.NewConfigHandler(configs, sess).Register(router) - handler.NewSiteHandler(users, repos, commits, perms, sess, templ.ExecuteTemplate).Register(router) + handler.NewServerHandler(servers, sess).Register(router) + handler.NewRemoteHandler(users, remotes, sess).Register(router) + handler.NewWsHandler(repos, commits, perms, sess, pubsub).Register(router) + //handler.NewSiteHandler(users, repos, commits, perms, sess, templ.ExecuteTemplate).Register(router) - // serve static assets - // TODO we need to replace this with go.rice - http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(rice.MustFindBox("static/").HTTPBox()))) - - // server websocket data - http.Handle("/feed", websocket.Handler(channel.Read)) - - // register the router - // TODO we disabled nosurf because it was impacting API calls. - // we need to disable nosurf for api calls (ie not coming from website). - http.Handle("/", router) + box := rice.MustFindBox("app/") + fserver := http.FileServer(box.HTTPBox()) + index, _ := box.Bytes("index.html") + http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + switch { + case strings.HasPrefix(r.URL.Path, "/favicon.ico"), + strings.HasPrefix(r.URL.Path, "/scripts/"), + strings.HasPrefix(r.URL.Path, "/styles/"), + strings.HasPrefix(r.URL.Path, "/views/"): + fserver.ServeHTTP(w, r) + case strings.HasPrefix(r.URL.Path, "/logout"), + strings.HasPrefix(r.URL.Path, "/login/"), + strings.HasPrefix(r.URL.Path, "/v1/"), + strings.HasPrefix(r.URL.Path, "/ws/"): + router.ServeHTTP(w, r) + default: + w.Write(index) + } + }) // start webserver using HTTPS or HTTP - if len(sslcert) != 0 && len(sslkey) != 0 { + if len(sslcert) != 0 { panic(http.ListenAndServeTLS(port, sslcert, sslkey, nil)) } else { panic(http.ListenAndServe(port, nil)) } } +func setupDatabase() { + +} + +func setupQueue() { + +} + +func setupHandlers() { + +} + // initialize the .drone directory and create a skeleton config // file if one does not already exist. func init() { diff --git a/server/pubsub/buffer.go b/server/pubsub/buffer.go new file mode 100644 index 00000000..d3c56091 --- /dev/null +++ b/server/pubsub/buffer.go @@ -0,0 +1,30 @@ +package pubsub + +import ( + "bytes" +) + +type Buffer struct { + buf bytes.Buffer + channel *Channel +} + +func NewBuffer(channel *Channel) *Buffer { + return &Buffer{ + channel: channel, + } +} + +func (b *Buffer) Write(p []byte) (n int, err error) { + n, err = b.buf.Write(p) + b.channel.Publish(p) + return +} + +func (b *Buffer) WriteString(s string) (n int, err error) { + return b.Write([]byte(s)) +} + +func (b *Buffer) Bytes() []byte { + return b.buf.Bytes() +} diff --git a/server/pubsub/channel.go b/server/pubsub/channel.go new file mode 100644 index 00000000..64b42d15 --- /dev/null +++ b/server/pubsub/channel.go @@ -0,0 +1,118 @@ +package pubsub + +import ( + "time" +) + +type Channel struct { + record bool + history []interface{} + timeout time.Duration + closed chan bool + broadcast chan interface{} + subscribe chan *Subscription + unsubscribe chan *Subscription + subscriptions map[*Subscription]bool +} + +func NewChannel(opts *Opts) *Channel { + return &Channel{ + timeout: opts.Timeout, + record: opts.Record, + history: make([]interface{}, 0), + closed: make(chan bool), + broadcast: make(chan interface{}), + subscribe: make(chan *Subscription), + unsubscribe: make(chan *Subscription), + subscriptions: make(map[*Subscription]bool), + } +} + +func (c *Channel) Publish(data interface{}) { + c.broadcast <- data + return +} + +func (c *Channel) Subscribe() *Subscription { + s := NewSubscription(c) + c.subscribe <- s + return s +} + +func (c *Channel) Close() { + go func() { c.closed <- true }() +} + +func (c *Channel) start() { + // make sure we don't bring down the application + // if somehow we encounter a nil pointer or some + // other unexpected behavior. + defer func() { + recover() + }() + + // timeout the channel after N duration + // ignore the timeout if set to 0 + var timeout <-chan time.Time + if c.timeout > 0 { + timeout = time.After(c.timeout) + } + + for { + select { + + case sub := <-c.unsubscribe: + delete(c.subscriptions, sub) + close(sub.send) + + case sub := <-c.subscribe: + c.subscriptions[sub] = true + + // if we are recording the output + // we should send it to the subscriber + // upon first connecting. + if c.record && len(c.history) > 0 { + history := make([]interface{}, len(c.history)) + copy(history, c.history) + go replay(sub, history) + } + + case msg := <-c.broadcast: + // if we are recording the output, append + // the message to the history + if c.record { + c.history = append(c.history, msg) + } + + // loop through each subscription and + // send the message. + for sub := range c.subscriptions { + select { + case sub.send <- msg: + // do nothing + default: + sub.Close() + } + } + + case <-timeout: + c.Close() + + case <-c.closed: + c.stop() + return + } + } +} + +func replay(s *Subscription, history []interface{}) { + for _, msg := range history { + s.send <- msg + } +} + +func (c *Channel) stop() { + for sub := range c.subscriptions { + sub.Close() + } +} diff --git a/server/pubsub/opts.go b/server/pubsub/opts.go new file mode 100644 index 00000000..19499f91 --- /dev/null +++ b/server/pubsub/opts.go @@ -0,0 +1,21 @@ +package pubsub + +import ( + "time" +) + +type Opts struct { + // Timeout sets the expiration date for the channel, + // at which time it will be closed and transmission will + // stop. A zero value for means the channel will not timeout. + Timeout time.Duration + + // Record indicates the channel should record the channel + // activity and playback the full history to subscribers. + Record bool +} + +var DefaultOpts = &Opts{ + Timeout: 0, + Record: false, +} diff --git a/server/pubsub/pubsub.go b/server/pubsub/pubsub.go new file mode 100644 index 00000000..41bd7acd --- /dev/null +++ b/server/pubsub/pubsub.go @@ -0,0 +1,75 @@ +package pubsub + +import ( + "sync" +) + +type PubSub struct { + sync.Mutex + + // In-memory list of all channels being managed by the broker. + channels map[interface{}]*Channel +} + +// NewPubSub creates a new instance of the PubSub type +// and returns a pointer. +func NewPubSub() *PubSub { + return &PubSub{ + channels: make(map[interface{}]*Channel), + } +} + +// Lookup performs a thread safe operation to return a pointer +// to an existing Channel object with the given key. If the +// Channel does not exist a nil value is returned. +func (b *PubSub) Lookup(key interface{}) *Channel { + b.Lock() + defer b.Unlock() + + // find the channel in the existing list + return b.channels[key] +} + +// Register performs a thread safe operation to return a pointer +// to a Channel object for the given key. The Channel is created +// if it does not yet exist. +func (b *PubSub) Register(key interface{}) *Channel { + return b.RegisterOpts(key, DefaultOpts) +} + +// Register performs a thread safe operation to return a pointer +// to a Channel object for the given key. The Channel is created +// if it does not yet exist using custom options. +func (b *PubSub) RegisterOpts(key interface{}, opts *Opts) *Channel { + b.Lock() + defer b.Unlock() + + // find the channel in the existing list + c, ok := b.channels[key] + if ok { + return c + } + + // create the channel and register + // with the pubsub server + c = NewChannel(opts) + b.channels[key] = c + go c.start() + return c +} + +// Unregister performs a thread safe operation to delete the +// Channel with the given key. +func (b *PubSub) Unregister(key interface{}) { + b.Lock() + defer b.Unlock() + + // find the channel in the existing list + c, ok := b.channels[key] + if !ok { + return + } + c.Close() + delete(b.channels, key) + return +} diff --git a/server/pubsub/subscribe.go b/server/pubsub/subscribe.go new file mode 100644 index 00000000..f88cba2b --- /dev/null +++ b/server/pubsub/subscribe.go @@ -0,0 +1,28 @@ +package pubsub + +type Subscription struct { + channel *Channel + closed chan bool + send chan interface{} +} + +func NewSubscription(channel *Channel) *Subscription { + return &Subscription{ + channel: channel, + closed: make(chan bool), + send: make(chan interface{}), + } +} + +func (s *Subscription) Read() <-chan interface{} { + return s.send +} + +func (s *Subscription) Close() { + go func() { s.channel.unsubscribe <- s }() + go func() { s.closed <- true }() +} + +func (s *Subscription) CloseNotify() <-chan bool { + return s.closed +} diff --git a/server/queue/build_runner.go b/server/queue/build_runner.go deleted file mode 100644 index 15741356..00000000 --- a/server/queue/build_runner.go +++ /dev/null @@ -1,41 +0,0 @@ -package queue - -import ( - "io" - "time" - - "github.com/drone/drone/shared/build" - "github.com/drone/drone/shared/build/docker" - "github.com/drone/drone/shared/build/repo" - "github.com/drone/drone/shared/build/script" -) - -type BuildRunner interface { - Run(buildScript *script.Build, repo *repo.Repo, key []byte, privileged bool, buildOutput io.Writer) (success bool, err error) -} - -type buildRunner struct { - dockerClient *docker.Client - timeout time.Duration -} - -func NewBuildRunner(dockerClient *docker.Client, timeout time.Duration) BuildRunner { - return &buildRunner{ - dockerClient: dockerClient, - timeout: timeout, - } -} - -func (runner *buildRunner) Run(buildScript *script.Build, repo *repo.Repo, key []byte, privileged bool, buildOutput io.Writer) (bool, error) { - builder := build.New(runner.dockerClient) - builder.Build = buildScript - builder.Repo = repo - builder.Key = key - builder.Privileged = privileged - builder.Stdout = buildOutput - builder.Timeout = runner.timeout - - err := builder.Run() - - return builder.BuildState == nil || builder.BuildState.ExitCode != 0, err -} diff --git a/server/queue/queue.go b/server/queue/queue.go deleted file mode 100644 index 299c9810..00000000 --- a/server/queue/queue.go +++ /dev/null @@ -1,47 +0,0 @@ -package queue - -import ( - "github.com/drone/drone/server/database" - "github.com/drone/drone/shared/model" - - "github.com/drone/drone/shared/build/script" -) - -// A Queue dispatches tasks to workers. -type Queue struct { - tasks chan<- *BuildTask -} - -// BuildTasks represents a build that is pending -// execution. -type BuildTask struct { - User *model.User - Repo *model.Repo - Commit *model.Commit - - // Build instructions from the .drone.yml - // file, unmarshalled. - Script *script.Build -} - -// Start N workers with the given build runner. -func Start(workers int, commits database.CommitManager, runner BuildRunner) *Queue { - tasks := make(chan *BuildTask) - queue := &Queue{tasks: tasks} - - for i := 0; i < workers; i++ { - worker := worker{ - runner: runner, - commits: commits, - } - - go worker.work(tasks) - } - - return queue -} - -// Add adds the task to the build queue. -func (q *Queue) Add(task *BuildTask) { - q.tasks <- task -} diff --git a/server/queue/worker.go b/server/queue/worker.go deleted file mode 100644 index da63fbc6..00000000 --- a/server/queue/worker.go +++ /dev/null @@ -1,260 +0,0 @@ -package queue - -import ( - "bytes" - "fmt" - "github.com/drone/drone/server/channel" - "github.com/drone/drone/server/database" - "github.com/drone/drone/shared/build/git" - "github.com/drone/drone/shared/build/repo" - "github.com/drone/drone/shared/build/script" - "github.com/drone/drone/shared/model" - "io" - "log" - "path/filepath" - "time" -) - -type worker struct { - commits database.CommitManager - - runner BuildRunner -} - -// work is a function that will infinitely -// run in the background waiting for tasks that -// it can pull off the queue and execute. -func (w *worker) work(queue <-chan *BuildTask) { - var task *BuildTask - for { - // get work item (pointer) from the queue - task = <-queue - if task == nil { - continue - } - - // execute the task - err := w.execute(task) - if err != nil { - log.Println(err) - } - } -} - -// execute will execute the build task and persist -// the results to the datastore. -func (w *worker) execute(task *BuildTask) error { - // we need to be sure that we can recover - // from any sort panic that could occur - // to avoid brining down the entire application - defer func() { - if e := recover(); e != nil { - task.Commit.Finished = time.Now().Unix() - task.Commit.Duration = task.Commit.Finished - task.Commit.Started - task.Commit.Status = model.StatusError - w.commits.Update(task.Commit) - } - }() - - // parse the build script - params, err := task.Repo.ParamMap() - task.Script, err = script.ParseBuild(task.Commit.Config, params) - if err != nil { - log.Printf("Error parsing repository params. %s\n", err) - return err - } - - // update commit and build status - task.Commit.Status = model.StatusStarted - task.Commit.Started = time.Now().Unix() - - // persist the commit to the database - if err := w.commits.Update(task.Commit); err != nil { - log.Printf("Error updating commit. %s\n", err) - return err - } - - // get settings - //settings, _ := database.GetSettings() - - // notification context - //context := ¬ify.Context{ - // Repo: task.Repo, - // Commit: task.Commit, - // Host: settings.URL().String(), - //} - - // send all "started" notifications - //if task.Script.Notifications != nil { - // task.Script.Notifications.Send(context) - //} - - // Send "started" notification to Github - //if err := updateGitHubStatus(task.Repo, task.Commit); err != nil { - // log.Printf("error updating github status: %s\n", err.Error()) - //} - - // make sure a channel exists for the repository, - // the commit, and the commit output (TODO) - reposlug := fmt.Sprintf("%s/%s/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name) - //commitslug := fmt.Sprintf("%s/%s/%s/%s/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name, task.Commit.Branch, task.Commit.Sha) - consoleslug := fmt.Sprintf("%s/%s/%s/%s/%s", task.Repo.Host, task.Repo.Owner, task.Repo.Name, task.Commit.Branch, task.Commit.Sha) - channel.Create(reposlug) - //channel.Create(commitslug) - channel.CreateStream(consoleslug) - - // notify the channels that the commit and build started - channel.SendJSON(reposlug, task.Commit) - //channel.SendJSON(commitslug, task.Commit) - - var buf = &bufferWrapper{channel: consoleslug} - - // append private parameters to the environment - // variable section of the .drone.yml file, iff - // this is not a pull request (for security purposes) - //if task.Repo.Params != nil && len(task.Commit.PullRequest) == 0 { - // for k, v := range task.Repo.Params { - // task.Script.Env = append(task.Script.Env, k+"="+v) - // } - //} - - //defer func() { - // // update the status of the commit using the - // // GitHub status API. - // if err := updateGitHubStatus(task.Repo, task.Commit); err != nil { - // log.Printf("error updating github status: %s\n", err.Error()) - // } - //}() - - // execute the build - passed, buildErr := w.runBuild(task, buf) - - task.Commit.Finished = time.Now().Unix() - task.Commit.Duration = task.Commit.Finished - task.Commit.Started - task.Commit.Status = model.StatusSuccess - - // capture build output - stdout := buf.buf.String() - - // if exit code != 0 set to failure - if passed { - task.Commit.Status = model.StatusFailure - if buildErr != nil && len(stdout) == 0 { - // TODO: If you wanted to have very friendly error messages, you could do that here - stdout = fmt.Sprintf("%s\n", buildErr.Error()) - } - } - - // persist the build output - if err := w.commits.UpdateOutput(task.Commit, []byte(stdout)); err != nil { - return nil - } - - // persist the commit to the database - if err := w.commits.Update(task.Commit); err != nil { - return err - } - - // notify the channels that the commit and build finished - channel.SendJSON(reposlug, task.Commit) - //channel.SendJSON(commitslug, task.Commit) - channel.Close(consoleslug) - - // send all "finished" notifications - //if task.Script.Notifications != nil { - // task.Script.Notifications.Send(context) - //} - - return nil -} - -func (w *worker) runBuild(task *BuildTask, buf io.Writer) (bool, error) { - var path = filepath.Join(task.Repo.Host, task.Repo.Owner, task.Repo.Name) - path = git.GitPath(task.Script.Git, path) - - repo := &repo.Repo{ - Name: task.Repo.Host + task.Repo.Owner + task.Repo.Name, - Path: task.Repo.CloneURL, - Branch: task.Commit.Branch, - Commit: task.Commit.Sha, - PR: task.Commit.PullRequest, - //TODO the builder should handle this - Dir: filepath.Join("/var/cache/drone/src", path), - Depth: git.GitDepth(task.Script.Git), - } - - if task.Repo.Private { - repo.Path = task.Repo.SSHURL - } - - return w.runner.Run( - task.Script, - repo, - []byte(task.Repo.PrivateKey), - task.Repo.Privileged, - buf, - ) -} - -// updateGitHubStatus is a helper function that will send -// the build status to GitHub using the Status API. -// see https://github.com/blog/1227-commit-status-api -func updateGitHubStatus(repo *repo.Repo, commit *model.Commit) error { - /* - // convert from drone status to github status - var message, status string - switch commit.Status { - case "Success": - status = "success" - message = "The build succeeded on drone.io" - case "Failure": - status = "failure" - message = "The build failed on drone.io" - case "Started": - status = "pending" - message = "The build is pending on drone.io" - default: - status = "error" - message = "The build errored on drone.io" - } - - // get the system settings - settings, _ := database.GetSettings() - - // get the user from the database - // since we need his / her GitHub token - user, err := database.GetUser(repo.UserID) - if err != nil { - return err - } - - client := github.New(user.GithubToken) - client.ApiUrl = settings.GitHubApiUrl - buildUrl := getBuildUrl(settings.URL().String(), repo, commit) - - return client.Repos.CreateStatus(repo.Owner, repo.Name, status, buildUrl, message, commit.Hash) - */ - return nil -} - -/* -func getBuildUrl(host string, repo *repo.Repo, commit *commit.Commit) string { - branchQuery := url.Values{} - branchQuery.Set("branch", commit.Branch) - buildUrl := fmt.Sprintf("%s/%s/commit/%s?%s", host, repo.Slug, commit.Hash, branchQuery.Encode()) - return buildUrl -} -*/ - -type bufferWrapper struct { - buf bytes.Buffer - - // name of the channel - channel string -} - -func (b *bufferWrapper) Write(p []byte) (n int, err error) { - n, err = b.buf.Write(p) - channel.SendBytes(b.channel, p) - return -} diff --git a/server/template/400.amber b/server/template/400.amber deleted file mode 100644 index 536f5d47..00000000 --- a/server/template/400.amber +++ /dev/null @@ -1,4 +0,0 @@ -extends base -block content - div - | 400 BAD REQUEST! \ No newline at end of file diff --git a/server/template/401.amber b/server/template/401.amber deleted file mode 100644 index f0f158e7..00000000 --- a/server/template/401.amber +++ /dev/null @@ -1,4 +0,0 @@ -extends base -block content - div - | 401 NOT AUTHORIZED \ No newline at end of file diff --git a/server/template/403.amber b/server/template/403.amber deleted file mode 100644 index b02e37c0..00000000 --- a/server/template/403.amber +++ /dev/null @@ -1,4 +0,0 @@ -extends base -block content - div - | 403 FORBIDDEN \ No newline at end of file diff --git a/server/template/404.amber b/server/template/404.amber deleted file mode 100644 index 96814f7d..00000000 --- a/server/template/404.amber +++ /dev/null @@ -1,4 +0,0 @@ -extends base -block content - div - | 404 NOT FOUND \ No newline at end of file diff --git a/server/template/admin_conf.amber b/server/template/admin_conf.amber deleted file mode 100644 index 37ee8661..00000000 --- a/server/template/admin_conf.amber +++ /dev/null @@ -1,6 +0,0 @@ -extends base -block link - $link = "/admin/settings" -block content - div - | admin config \ No newline at end of file diff --git a/server/template/admin_users.amber b/server/template/admin_users.amber deleted file mode 100644 index 4011e477..00000000 --- a/server/template/admin_users.amber +++ /dev/null @@ -1,6 +0,0 @@ -extends base -block link - $link = "/admin/users" -block content - div - | admin users \ No newline at end of file diff --git a/server/template/base.amber b/server/template/base.amber deleted file mode 100644 index 1634c3da..00000000 --- a/server/template/base.amber +++ /dev/null @@ -1,33 +0,0 @@ -doctype 5 -html - head - meta[charset="utf-8"] - meta[name="author"][content="Brad Rydzewski"] - link[rel="shortcut icon"][href="/static/images/favicon.png"] - link[rel="stylesheet"][href="//yui.yahooapis.com/pure/0.4.2/pure-min.css"] - link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Open+Sans"] - link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Orbitron"] - link[rel="stylesheet"][href="//fonts.googleapis.com/css?family=Droid+Sans+Mono"] - link[rel="stylesheet"][href="//netdna.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css"] - link[rel="stylesheet"][href="/static/styles/drone.css"] - title - block title - body - header - a.header-brand[href="/"] - | Drone - if User - a[href="/account/profile"].header-user - img.header-avatar[src="//secure.gravatar.com/avatar/"+User.Gravatar+"?s=64"] - span.header-username #{User.Login} - else - a[href="/login"].header-login - | Login - - - div - block content - block scripts - script[src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"] - script[src="//cdnjs.cloudflare.com/ajax/libs/jquery-timeago/1.1.0/jquery.timeago.js"] - script[src="/static/scripts/main.js"] \ No newline at end of file diff --git a/server/template/html/400.html b/server/template/html/400.html deleted file mode 100644 index 2c7dece3..00000000 --- a/server/template/html/400.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
400 BAD REQUEST!
-
- - - - - diff --git a/server/template/html/401.html b/server/template/html/401.html deleted file mode 100644 index 571303f5..00000000 --- a/server/template/html/401.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
401 NOT AUTHORIZED
-
- - - - - diff --git a/server/template/html/403.html b/server/template/html/403.html deleted file mode 100644 index 6a391c7b..00000000 --- a/server/template/html/403.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
403 FORBIDDEN
-
- - - - - diff --git a/server/template/html/404.html b/server/template/html/404.html deleted file mode 100644 index a238b794..00000000 --- a/server/template/html/404.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
404 NOT FOUND
-
- - - - - diff --git a/server/template/html/admin_conf.html b/server/template/html/admin_conf.html deleted file mode 100644 index 175ceb18..00000000 --- a/server/template/html/admin_conf.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
admin config
-
- - - - - diff --git a/server/template/html/admin_users.html b/server/template/html/admin_users.html deleted file mode 100644 index cedba3c7..00000000 --- a/server/template/html/admin_users.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
admin users
-
- - - - - diff --git a/server/template/html/base.html b/server/template/html/base.html deleted file mode 100644 index c00aa377..00000000 --- a/server/template/html/base.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
- - - - - diff --git a/server/template/html/index.html b/server/template/html/index.html deleted file mode 100644 index f7e99a75..00000000 --- a/server/template/html/index.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone - {{ if .User }} - - - {{ .User.Login}} - - {{ end }} -
-
- - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/server/template/html/login.html b/server/template/html/login.html deleted file mode 100644 index fa6120f2..00000000 --- a/server/template/html/login.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - - - - Login - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
- -
- - - - - diff --git a/server/template/html/repo_branch.html b/server/template/html/repo_branch.html deleted file mode 100644 index aa5d06ab..00000000 --- a/server/template/html/repo_branch.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - {{$__amber_1 := .Repo.Name}}{{$__amber_1}} · {{.Branch}} - - - -
- Drone{{if .User}} - {{$__amber_2 := .User.Gravatar}}{{$__amber_3 := __amber_add "//secure.gravatar.com/avatar/" $__amber_2}}{{$__amber_4 := __amber_add $__amber_3 "?s=64"}} - - {{$__amber_5 := .User.Login}}{{$__amber_5}} - {{else}} - Login{{end}} -
-
-
-
-

- {{$__amber_6 := .Repo.Owner}}{{$__amber_6}} - /{{$__amber_7 := .Repo.Name}}{{$__amber_8 := .Repo.Owner}}{{$__amber_9 := .Repo.Host}}{{$__amber_10 := __amber_add "/" $__amber_9}}{{$__amber_11 := __amber_add $__amber_10 "/"}}{{$__amber_12 := __amber_add $__amber_11 $__amber_8}}{{$__amber_13 := __amber_add $__amber_12 "/"}}{{$__amber_14 := __amber_add $__amber_13 $__amber_7}} - {{$__amber_15 := .Repo.Name}}{{$__amber_15}} - / - {{.Branch}} -

{{$__amber_16 := .Repo.Name}}{{$__amber_17 := .Repo.Owner}}{{$__amber_18 := .Repo.Host}}{{$__amber_19 := __amber_add "/" $__amber_18}}{{$__amber_20 := __amber_add $__amber_19 "/"}}{{$__amber_21 := __amber_add $__amber_20 $__amber_17}}{{$__amber_22 := __amber_add $__amber_21 "/"}}{{$__amber_23 := __amber_add $__amber_22 $__amber_16}}{{$__amber_24 := __amber_add $__amber_23 "/settings"}} - - - -
-
{{$repo := .Repo}}{{$branch := .Branch}} -
{{range $commit := .Commits}}{{$__amber_25 := $repo.Name}}{{$__amber_26 := $repo.Owner}}{{$__amber_27 := $repo.Host}}{{$__amber_28 := __amber_add "/" $__amber_27}}{{$__amber_29 := __amber_add $__amber_28 "/"}}{{$__amber_30 := __amber_add $__amber_29 $__amber_26}}{{$__amber_31 := __amber_add $__amber_30 "/"}}{{$__amber_32 := __amber_add $__amber_31 $__amber_25}}{{$__amber_33 := __amber_add $__amber_32 "/branch/"}}{{$__amber_34 := __amber_add $__amber_33 .Branch}}{{$__amber_35 := __amber_add $__amber_34 "/commit/"}}{{$__amber_36 := __amber_add $__amber_35 .Sha}} - -
{{$__amber_37 := __amber_add "https://secure.gravatar.com/avatar/" .Gravatar}}{{$__amber_38 := __amber_add $__amber_37 "?s=48&d=identicon"}} - -
-
-

{{.Message}}

- {{.ShaShort}} - {{.Branch}} - -
-
{{end}} -
-
-
-
-
- Recent Branches -
    {{range $commit := .Branches}}{{$__amber_39 := __amber_eql $branch .Branch}} - {{$__amber_40 := $repo.Name}}{{$__amber_41 := $repo.Owner}}{{$__amber_42 := $repo.Host}}{{$__amber_43 := __amber_add "/" $__amber_42}}{{$__amber_44 := __amber_add $__amber_43 "/"}}{{$__amber_45 := __amber_add $__amber_44 $__amber_41}}{{$__amber_46 := __amber_add $__amber_45 "/"}}{{$__amber_47 := __amber_add $__amber_46 $__amber_40}}{{$__amber_48 := __amber_add $__amber_47 "/branch/"}}{{$__amber_49 := __amber_add $__amber_48 .Branch}} - {{.Branch}} - {{end}} -
-
-
-
-
-
- - - - - - - diff --git a/server/template/html/repo_commit.html b/server/template/html/repo_commit.html deleted file mode 100644 index 66837138..00000000 --- a/server/template/html/repo_commit.html +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - {{$__amber_1 := .Repo.Name}}{{$__amber_2 := .Commit.Sha}}{{$__amber_1}} · {{$__amber_2}} - - - -
- Drone{{if .User}} - {{$__amber_3 := .User.Gravatar}}{{$__amber_4 := __amber_add "//secure.gravatar.com/avatar/" $__amber_3}}{{$__amber_5 := __amber_add $__amber_4 "?s=64"}} - - {{$__amber_6 := .User.Login}}{{$__amber_6}} - {{else}} - Login{{end}} -
-
-
-
-

- {{$__amber_7 := .Repo.Owner}}{{$__amber_7}} - /{{$__amber_8 := .Repo.Name}}{{$__amber_9 := .Repo.Owner}}{{$__amber_10 := .Repo.Host}}{{$__amber_11 := __amber_add "/" $__amber_10}}{{$__amber_12 := __amber_add $__amber_11 "/"}}{{$__amber_13 := __amber_add $__amber_12 $__amber_9}}{{$__amber_14 := __amber_add $__amber_13 "/"}}{{$__amber_15 := __amber_add $__amber_14 $__amber_8}} - {{$__amber_16 := .Repo.Name}}{{$__amber_16}} - / - {{$__amber_17 := .Commit.ShaShort}}{{$__amber_17}} -

{{$__amber_18 := .Repo.Name}}{{$__amber_19 := .Repo.Owner}}{{$__amber_20 := .Repo.Host}}{{$__amber_21 := __amber_add "/" $__amber_20}}{{$__amber_22 := __amber_add $__amber_21 "/"}}{{$__amber_23 := __amber_add $__amber_22 $__amber_19}}{{$__amber_24 := __amber_add $__amber_23 "/"}}{{$__amber_25 := __amber_add $__amber_24 $__amber_18}} - - - -
-
{{$__amber_26 := .Commit.Status}} -
- commit - {{$__amber_27 := .Commit.ShaShort}}{{$__amber_27}} - to - {{$__amber_28 := .Commit.Branch}}{{$__amber_28}} - branch -
-
{{$__amber_29 := .Commit.Status}} -

-				
-
-
-
{{$__amber_30 := .Commit.Gravatar}}{{$__amber_31 := __amber_add "//secure.gravatar.com/avatar/" $__amber_30}}{{$__amber_32 := __amber_add $__amber_31 "?s=64&d=identicon"}} - -
-

{{$__amber_33 := .Commit.ShaShort}}{{$__amber_33}}

-

{{$__amber_34 := .Commit.Branch}}{{$__amber_34}}

-
-
- finished:{{$__amber_35 := .Commit.FinishedString}} - -
-
- duration: - {{$__amber_36 := .Commit.Duration}}{{$__amber_36}} seconds -
-
- message: -

{{$__amber_37 := .Commit.Message}}{{$__amber_37}}

-
-
-
-
-
-
- - - - {{$__amber_38 := .Commit.Status}}{{$__amber_39 := __amber_eql $__amber_38 "Pending"}}{{$__amber_40 := .Commit.Status}}{{$__amber_41 := __amber_eql $__amber_40 "Started"}}{{$__amber_42 := or $__amber_41 $__amber_39}}{{if $__amber_42}} - - {{else}} - {{end}} - - diff --git a/server/template/html/repo_conf.html b/server/template/html/repo_conf.html deleted file mode 100644 index afd34593..00000000 --- a/server/template/html/repo_conf.html +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - {{$__amber_1 := .Repo.Name}}{{$__amber_1}} · Config - - - -
- Drone{{if .User}} - {{$__amber_2 := .User.Gravatar}}{{$__amber_3 := __amber_add "//secure.gravatar.com/avatar/" $__amber_2}}{{$__amber_4 := __amber_add $__amber_3 "?s=64"}} - - {{$__amber_5 := .User.Login}}{{$__amber_5}} - {{else}} - Login{{end}} -
-
-
-
-

- {{$__amber_6 := .Repo.Owner}}{{$__amber_6}} - /{{$__amber_7 := .Repo.Name}}{{$__amber_8 := .Repo.Owner}}{{$__amber_9 := .Repo.Host}}{{$__amber_10 := __amber_add "/" $__amber_9}}{{$__amber_11 := __amber_add $__amber_10 "/"}}{{$__amber_12 := __amber_add $__amber_11 $__amber_8}}{{$__amber_13 := __amber_add $__amber_12 "/"}}{{$__amber_14 := __amber_add $__amber_13 $__amber_7}} - {{$__amber_15 := .Repo.Name}}{{$__amber_15}} - / - settings -

{{$__amber_16 := .Repo.Name}}{{$__amber_17 := .Repo.Owner}}{{$__amber_18 := .Repo.Host}}{{$__amber_19 := __amber_add "/" $__amber_18}}{{$__amber_20 := __amber_add $__amber_19 "/"}}{{$__amber_21 := __amber_add $__amber_20 $__amber_17}}{{$__amber_22 := __amber_add $__amber_21 "/"}}{{$__amber_23 := __amber_add $__amber_22 $__amber_16}} - - - -
-
{{$__amber_24 := .Repo.Name}}{{$__amber_25 := .Repo.Owner}}{{$__amber_26 := .Repo.Host}}{{$__amber_27 := __amber_add "/v1/repos/" $__amber_26}}{{$__amber_28 := __amber_add $__amber_27 "/"}}{{$__amber_29 := __amber_add $__amber_28 $__amber_25}}{{$__amber_30 := __amber_add $__amber_29 "/"}}{{$__amber_31 := __amber_add $__amber_30 $__amber_24}} -
-
-
- Commit Hooks -
-
{{$__amber_32 := .Repo.PostCommit}} - - -
- Post Commit Hooks -
-
-
{{$__amber_33 := .Repo.PullRequest}} - - -
- Pull Request Hooks -
-
-
- Badges - -
-
- Private Variables - -
-
- Public Key - -
-
- - -
-
-
-
-
-
- - - - - - diff --git a/server/template/html/repo_feed.html b/server/template/html/repo_feed.html deleted file mode 100644 index bd5bbdf5..00000000 --- a/server/template/html/repo_feed.html +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - {{$__amber_1 := .Repo.Name}}{{$__amber_1}} - - - -
- Drone{{if .User}} - {{$__amber_2 := .User.Gravatar}}{{$__amber_3 := __amber_add "//secure.gravatar.com/avatar/" $__amber_2}}{{$__amber_4 := __amber_add $__amber_3 "?s=64"}} - - {{$__amber_5 := .User.Login}}{{$__amber_5}} - {{else}} - Login{{end}} -
-
-
-
-

- {{$__amber_6 := .Repo.Owner}}{{$__amber_6}} - / - {{$__amber_7 := .Repo.Name}}{{$__amber_7}} -

{{$__amber_8 := .Repo.Name}}{{$__amber_9 := .Repo.Owner}}{{$__amber_10 := .Repo.Host}}{{$__amber_11 := __amber_add "/" $__amber_10}}{{$__amber_12 := __amber_add $__amber_11 "/"}}{{$__amber_13 := __amber_add $__amber_12 $__amber_9}}{{$__amber_14 := __amber_add $__amber_13 "/"}}{{$__amber_15 := __amber_add $__amber_14 $__amber_8}}{{$__amber_16 := __amber_add $__amber_15 "/settings"}} - - - -
{{$repo := .Repo}}{{$__amber_17 := .Repo.Active}}{{if $__amber_17}}{{if .Commits}} -
-
{{range $commit := .Commits}}{{$__amber_18 := $repo.Name}}{{$__amber_19 := $repo.Owner}}{{$__amber_20 := $repo.Host}}{{$__amber_21 := __amber_add "/" $__amber_20}}{{$__amber_22 := __amber_add $__amber_21 "/"}}{{$__amber_23 := __amber_add $__amber_22 $__amber_19}}{{$__amber_24 := __amber_add $__amber_23 "/"}}{{$__amber_25 := __amber_add $__amber_24 $__amber_18}}{{$__amber_26 := __amber_add $__amber_25 "/branch/"}}{{$__amber_27 := __amber_add $__amber_26 .Branch}}{{$__amber_28 := __amber_add $__amber_27 "/commit/"}}{{$__amber_29 := __amber_add $__amber_28 .Sha}} - -
{{$__amber_30 := __amber_add "https://secure.gravatar.com/avatar/" .Gravatar}}{{$__amber_31 := __amber_add $__amber_30 "?s=48&d=identicon"}} - -
-
-

{{.Message}}

- {{.ShaShort}} - {{.Branch}} - -
-
{{end}} -
-
-
-
-
- Recent Branches -
    {{range $commit := .Branches}} -
  • {{$__amber_32 := $repo.Name}}{{$__amber_33 := $repo.Owner}}{{$__amber_34 := $repo.Host}}{{$__amber_35 := __amber_add "/" $__amber_34}}{{$__amber_36 := __amber_add $__amber_35 "/"}}{{$__amber_37 := __amber_add $__amber_36 $__amber_33}}{{$__amber_38 := __amber_add $__amber_37 "/"}}{{$__amber_39 := __amber_add $__amber_38 $__amber_32}}{{$__amber_40 := __amber_add $__amber_39 "/branch/"}}{{$__amber_41 := __amber_add $__amber_40 .Branch}} - {{.Branch}} -
  • {{end}} -
-
-
-
{{else}} -
-
Your Build history is empty. Push to your remote repository to trigger a build.
-
{{end}}{{else}} -
-
This Repository is not yet active. Please activate to start testing commits.
-
{{$__amber_42 := .Repo.Name}}{{$__amber_43 := .Repo.Owner}}{{$__amber_44 := .Repo.Host}}{{$__amber_45 := __amber_add "/v1/repos/" $__amber_44}}{{$__amber_46 := __amber_add $__amber_45 "/"}}{{$__amber_47 := __amber_add $__amber_46 $__amber_43}}{{$__amber_48 := __amber_add $__amber_47 "/"}}{{$__amber_49 := __amber_add $__amber_48 $__amber_42}} -
-
- -
-
-
{{end}} -
-
- - - - {{$__amber_50 := .Repo.Active}}{{if $__amber_50}} - {{else}} - {{end}} - - diff --git a/server/template/html/user_conf.html b/server/template/html/user_conf.html deleted file mode 100644 index f07806e5..00000000 --- a/server/template/html/user_conf.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - {{$__amber_1 := .User.Login}}{{$__amber_1}} · Profile - - - -
- Drone{{if .User}} - {{$__amber_2 := .User.Gravatar}}{{$__amber_3 := __amber_add "//secure.gravatar.com/avatar/" $__amber_2}}{{$__amber_4 := __amber_add $__amber_3 "?s=64"}} - - {{$__amber_5 := .User.Login}}{{$__amber_5}} - {{else}} - Login{{end}} -
-
- -
- - - - - diff --git a/server/template/html/user_feed.html b/server/template/html/user_feed.html deleted file mode 100644 index 373b07c7..00000000 --- a/server/template/html/user_feed.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - - - - - - - Build Feed - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
-
-

Build Feed

- - - -
-
{{if .Feed}} -
{{range $commit := .Feed}}{{$__amber_5 := __amber_add "/" .Remote}}{{$__amber_6 := __amber_add $__amber_5 "/"}}{{$__amber_7 := __amber_add $__amber_6 .Owner}}{{$__amber_8 := __amber_add $__amber_7 "/"}}{{$__amber_9 := __amber_add $__amber_8 .Name}}{{$__amber_10 := __amber_add $__amber_9 "/branch/"}}{{$__amber_11 := __amber_add $__amber_10 .Branch}}{{$__amber_12 := __amber_add $__amber_11 "/commit/"}}{{$__amber_13 := __amber_add $__amber_12 .Sha}} - -
{{$__amber_14 := __amber_add "https://secure.gravatar.com/avatar/" .Gravatar}}{{$__amber_15 := __amber_add $__amber_14 "?s=48&d=identicon"}} - -
-
-

- {{.Owner}} - / - {{.Name}} -

-

{{.Message}}

- {{.ShaShort}} - {{.Branch}} - -
-
{{end}} -
{{else}} -
- Your build feed is empty. - Setup - your repositories to get started. -
{{end}} -
-
-
- - - - - - diff --git a/server/template/html/user_login.html b/server/template/html/user_login.html deleted file mode 100644 index 9b2fe940..00000000 --- a/server/template/html/user_login.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
User Login
-
- - - - - diff --git a/server/template/html/user_repos.html b/server/template/html/user_repos.html deleted file mode 100644 index 05fe7164..00000000 --- a/server/template/html/user_repos.html +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - Repos - - - -
- Drone{{if .User}} - {{$__amber_1 := .User.Gravatar}}{{$__amber_2 := __amber_add "//secure.gravatar.com/avatar/" $__amber_1}}{{$__amber_3 := __amber_add $__amber_2 "?s=64"}} - - {{$__amber_4 := .User.Login}}{{$__amber_4}} - {{else}} - Login{{end}} -
-
-
-
-

My Repos

- - - -
-
-
- -
-
{{range $repo := .Repos}}{{$__amber_5 := __amber_add "/" .Host}}{{$__amber_6 := __amber_add $__amber_5 "/"}}{{$__amber_7 := __amber_add $__amber_6 .Owner}}{{$__amber_8 := __amber_add $__amber_7 "/"}}{{$__amber_9 := __amber_add $__amber_8 .Name}}{{$__amber_10 := __amber_add .Owner "/"}}{{$__amber_11 := __amber_add $__amber_10 .Name}} - -
-

- {{.Owner}} - / - {{.Name}} -

-
-
{{if .Active}} - On{{else}} - Off{{end}} -
-
{{end}} -
-
-
-
- - - - - - - diff --git a/server/template/login.amber b/server/template/login.amber deleted file mode 100644 index c4af218b..00000000 --- a/server/template/login.amber +++ /dev/null @@ -1,21 +0,0 @@ -extends base - -block title - | Login - -block content - article - header - h1 Login - section - section.pure-g - div.pure-u-1 - a[href="/login/github.com"] GitHub - div.pure-u-1 - a[href="/login/enterprise.github.com"] GitHub Enterprise - div.pure-u-1 - a[href="/login/bitbucket.org"] Bitbucket - div.pure-u-1 - a[href="/login/stash.atlassian.com"] Stash - div.pure-u-1 - a[href="/login/gitlab.com"] GitLab diff --git a/server/template/repo_branch.amber b/server/template/repo_branch.amber deleted file mode 100644 index dc6afc38..00000000 --- a/server/template/repo_branch.amber +++ /dev/null @@ -1,54 +0,0 @@ -extends base - -block title - | #{Repo.Name} · #{Branch} - -block content - article.pure-g - header.pure-u-1 - h1 - span #{Repo.Owner} - span / - a[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] #{Repo.Name} - span / - a[href="#"] #{Branch} - a.pure-button[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name+"/settings"] - i.fa.fa-cog - - section.pure-u-3-5 - $repo=Repo - $branch=Branch - div.commit-list - each $commit in Commits - a.pure-g.commit-item[data-status=Status][href="/"+$repo.Host+"/"+$repo.Owner+"/"+$repo.Name+"/branch/"+Branch+"/commit/"+Sha] - div.pure-u-1-8 - img[src="https://secure.gravatar.com/avatar/"+Gravatar+"?s=48&d=identicon"] - - div.pure-u-3-4 - h2 #{Message} - span.commit-sha #{ShaShort} - span.commit-branch #{Branch} - span.commit-date.timeago[title=FinishedString] - - div.pure-u-2-5 - section.pure-g.branches - div.pure-u-1.pure-menu.pure-menu-open - a.pure-menu-heading Recent Branches - ul - each $commit in Branches - li - .pure-menu-selected ? $branch == Branch - a[data-status=Status][href="/"+$repo.Host+"/"+$repo.Owner+"/"+$repo.Name+"/branch/"+Branch] #{Branch} - - -block append scripts - script - $(document).ready(function() { - $(".timeago").timeago(); - }); - - script - var ws = new WebSocket((window.location.protocol=='http:'?'ws':'wss')+'://'+window.location.host+'/feed?token='+#{Channel}); - ws.onmessage = function (e) { - console.log(e); - }; \ No newline at end of file diff --git a/server/template/repo_commit.amber b/server/template/repo_commit.amber deleted file mode 100644 index 69ba9de9..00000000 --- a/server/template/repo_commit.amber +++ /dev/null @@ -1,71 +0,0 @@ -extends base - -block title - | #{Repo.Name} · #{Commit.Sha} - -block content - article.pure-g - header.pure-u-1 - h1 - span #{Repo.Owner} - span / - a[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] #{Repo.Name} - span / - a[href="#"] #{Commit.ShaShort} - - a.pure-button[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] - i.fa.fa-dashboard - section.pure-u-3-4 - div.alert[data-status=Commit.Status] - | commit - u #{Commit.ShaShort} - | to - u #{Commit.Branch} - | branch - div.progress - - pre#stdout[data-status=Commit.Status] - div.pure-u-1-4.sticky - section.pure-g.build - div.pure-u-1 - img[src="//secure.gravatar.com/avatar/" +Commit.Gravatar+"?s=64&d=identicon"][style="border-radius:50%;float:left;margin-right:10px;"] - - header - h3 #{Commit.ShaShort} - h4 #{Commit.Branch} - div - span finished: - span.timeago[title=Commit.FinishedString] - div - span duration: - span #{Commit.Duration} seconds - - div - span message: - p #{Commit.Message} - -block append scripts - script - $(document).ready(function() { - $(".timeago").timeago(); - }); - - if Commit.Status == "Started" || Commit.Status == "Pending" - script - var ws = new WebSocket((window.location.protocol=='http:'?'ws':'wss')+'://'+window.location.host+'/feed?token=#{Channel}'); - ws.onmessage = function (e) { - console.log(e); - }; - script - $(document).ready(function() { - var commitUpdates = new Drone.CommitUpdates('/feed?token=#{Stream}'); - var outputBox = document.getElementById('stdout'); - commitUpdates.autoFollow = true; - commitUpdates.startOutput(outputBox); - }); - else - script - $.get("/v1/repos/"+#{Repo.Host}+"/"+#{Repo.Owner}+"/"+#{Repo.Name}+"/branches/"+#{Commit.Branch}+"/commits/"+#{Commit.Sha}+"/console", function( data ) { - var lineFormatter = new Drone.LineFormatter(); - $( "#stdout" ).html(lineFormatter.format(data)); - }); diff --git a/server/template/repo_conf.amber b/server/template/repo_conf.amber deleted file mode 100644 index 5f8a6a8a..00000000 --- a/server/template/repo_conf.amber +++ /dev/null @@ -1,80 +0,0 @@ -extends base - -block title - | #{Repo.Name} · Config - -block content - article.pure-g - header.pure-u-1 - h1 - span #{Repo.Owner} - span / - a[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] #{Repo.Name} - span / - a[href="#"] settings - a.pure-button[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] - i.fa.fa-dashboard - - section.pure-u-1 - form.pure-form.pure-form-stacked[action="/v1/repos/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] - fieldset - details[open] - summary Commit Hooks - div.pure-control-group - div.onoffswitch - input[type="checkbox"][name="post_commits"][checked=Repo.PostCommit].onoffswitch-checkbox#post_commits - label.onoffswitch-label[for="post_commits"] - div.onoffswitch-inner - div.onoffswitch-active - div.onoffswitch-switch ON - div.onoffswitch-inactive - div.onoffswitch-switch OFF - span Post Commit Hooks - div.pure-control-group - div.onoffswitch - input[type="checkbox"][name="pull_requests"][checked=Repo.PullRequest].onoffswitch-checkbox#pull_requests - label.onoffswitch-label[for="pull_requests"] - div.onoffswitch-inner - div.onoffswitch-active - div.onoffswitch-switch ON - div.onoffswitch-inactive - div.onoffswitch-switch OFF - span Pull Request Hooks - details - summary Badges - textarea[spellcheck="false"] [![Build Status](#{Scheme}://#{Host}/v1/badge/#{Repo.Host}/#{Repo.Owner}/#{Repo.Name}/status.svg)](#{Scheme}://#{Host}/#{Repo.Host}/#{Repo.Owner}/#{Repo.Name}) - details - summary Private Variables - textarea[spellcheck="false"][placeholder="FOO: BAR"][name="params"] #{Repo.Params} - details - summary Public Key - textarea[spellcheck="false"][rows="5"] #{Repo.PublicKey} - - div.pure-controls - button.pure-button.pure-button-primary[type="submit"] Save - button.pure-button.pure-button-secondary[type="reset"] Reset - -block append scripts - script - document.getElementById("post_commits").checked = #{Repo.PostCommit}; - document.getElementById("pull_requests").checked = #{Repo.PullRequest}; - document.forms[0].onsubmit = function(event) { - event.preventDefault(); - var form = event.target; - $.ajax({ - url: form.action, - type: 'PUT', - contentType: 'application/json', - data: JSON.stringify({ - post_commits: form.elements.post_commits.checked, - pull_requests: form.elements.pull_requests.checked, - params: form.elements.params.value - }), - dataType: 'json' - }).done(function() { - alert( "success" ); - }).fail(function() { - alert( "error" ); - }); - return false; - } \ No newline at end of file diff --git a/server/template/repo_feed.amber b/server/template/repo_feed.amber deleted file mode 100644 index cf41041d..00000000 --- a/server/template/repo_feed.amber +++ /dev/null @@ -1,84 +0,0 @@ -extends base - -block title - | #{Repo.Name} - -block content - article.pure-g - header.pure-u-1 - h1 - span #{Repo.Owner} - span / - a[href="#"] #{Repo.Name} - a.pure-button[href="/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name+"/settings"] - i.fa.fa-cog - - $repo=Repo - if Repo.Active - if Commits - section.pure-u-3-5 - div.commit-list - each $commit in Commits - a.pure-g.commit-item[data-status=Status][href="/"+$repo.Host+"/"+$repo.Owner+"/"+$repo.Name+"/branch/"+Branch+"/commit/"+Sha] - div.pure-u-1-8 - img[src="https://secure.gravatar.com/avatar/"+Gravatar+"?s=48&d=identicon"] - - div.pure-u-3-4 - h2 #{Message} - span.commit-sha #{ShaShort} - span.commit-branch #{Branch} - span.commit-date.timeago[title=FinishedString] - div.pure-u-2-5 - section.pure-g.branches - div.pure-u-1.pure-menu.pure-menu-open - a.pure-menu-heading Recent Branches - ul - each $commit in Branches - li - a[data-status=Status][href="/"+$repo.Host+"/"+$repo.Owner+"/"+$repo.Name+"/branch/"+Branch] #{Branch} - - else - section.pure-u-1 - div.alert.alert-info - | Your Build history is empty. Push to your remote repository to trigger a build. - - - else - section.pure-u-1 - div.alert.alert-info#alert - | This Repository is not yet active. Please activate to start testing commits. - br - form.pure-form.pure-form-stacked[action="/v1/repos/"+Repo.Host+"/"+Repo.Owner+"/"+Repo.Name] - div.pure-controls - button.pure-button.pure-button-primary Click to Active Repository - -block append scripts - script - $(document).ready(function() { - $(".timeago").timeago(); - }); - - if Repo.Active - script - var ws = new WebSocket((window.location.protocol=='http:'?'ws':'wss')+'://'+window.location.host+'/feed?token=#{Channel}'); - ws.onmessage = function (e) { - console.log(e); - }; - else - script - document.forms[0].onsubmit = function(event) { - var form = event.target; - var formData = new FormData(form); - xhr = new XMLHttpRequest(); - xhr.open('POST', form.action); - xhr.onload = function() { - if (this.status == 201) { - window.location.reload(); - } else { - $("#alert").text("Failed to activate the repository settings. Please try again"); - $("#alert").attr("class", "alert alert-danger"); - }; - }; - xhr.send(formData); - return false; - } \ No newline at end of file diff --git a/server/template/user_conf.amber b/server/template/user_conf.amber deleted file mode 100644 index 166dba49..00000000 --- a/server/template/user_conf.amber +++ /dev/null @@ -1,26 +0,0 @@ -extends base - -block title - | #{User.Login} · Profile - -block content - article - header - h1 Account Profile - section.user-account - section.pure-g - div.pure-u-1 - h3.user-remote[data-remote=User.Remote] Account - div.pure-u-1-6 - a.user-avatar[href="https://gravatar.com/"][target="_blank"] - img[src="https://gravatar.com/avatar/"+User.Gravatar+"?s=160&d=identicon"] - - div.pure-u-5-6 - h4.user-login #{User.Login} - div.user-fullname #{User.Name} - div.user-email #{User.Email} - - section - h3 API Key - form.pure-form - input.pure-u-1[type="text"][spellcheck="false"][value=User.Token] diff --git a/server/template/user_feed.amber b/server/template/user_feed.amber deleted file mode 100644 index 8e4553a2..00000000 --- a/server/template/user_feed.amber +++ /dev/null @@ -1,39 +0,0 @@ -extends base - -block title - | Build Feed - -block content - article - header - h1 Build Feed - a[href="/account/repos"].pure-button - i.fa.fa-plus - section - if Feed - div.activity-list - each $commit in Feed - a.pure-g.activity-item[data-status=Status][href="/"+Remote+"/"+Owner+"/"+Name+"/branch/"+Branch+"/commit/"+Sha] - div.pure-u-1-8 - img[src="https://secure.gravatar.com/avatar/" + Gravatar + "?s=48&d=identicon"] - - div.pure-u-3-4 - h2 - span #{Owner} - span.divider / - span #{Name} - p.activity-message #{Message} - span.activity-sha #{ShaShort} - span.activity-branch #{Branch} - span.activity-date.timeago[title=FinishedString] - else - div.alert.alert-info - | Your build feed is empty. - a[href="/account/repos"] Setup - | your repositories to get started. - -block append scripts - script - $(document).ready(function() { - $(".timeago").timeago(); - }); \ No newline at end of file diff --git a/server/template/user_login.amber b/server/template/user_login.amber deleted file mode 100644 index 8a5b498a..00000000 --- a/server/template/user_login.amber +++ /dev/null @@ -1,4 +0,0 @@ -extends base -block content - div - | User Login \ No newline at end of file diff --git a/server/template/user_repos.amber b/server/template/user_repos.amber deleted file mode 100644 index 5471a1cd..00000000 --- a/server/template/user_repos.amber +++ /dev/null @@ -1,40 +0,0 @@ -extends base - -block title - | Repos - -block content - article - header - h1 My Repos - a[href="/"].pure-button - i.fa.fa-rss - section - form.pure-form.search-form - input[type="search"][placeholder="Filter List"].pure-u-1#search - div.repo-list - each $repo in Repos - a.pure-g.repo-item.searchable[data-status=Active][href="/"+Host+"/"+Owner+"/"+Name][data-index=Owner+"/"+Name] - div.pure-u-11-12 - h2 - span #{Owner} - span.divider / - span #{Name} - div.pure-u-1-12 - if Active - span.repo-active On - else - span.repo-inactive Off - -block append scripts - style#search_style - script[type="text/javascript"] - var searchStyle = document.getElementById('search_style'); - document.getElementById('search').addEventListener('input', function() { - if (!this.value) { - searchStyle.innerHTML = ""; - return; - } - // look ma, no indexOf! - searchStyle.innerHTML = ".searchable:not([data-index*=\"" + this.value + "\"]) { display: none; }"; - }); diff --git a/server/worker/dispatch.go b/server/worker/dispatch.go new file mode 100644 index 00000000..76a1aeac --- /dev/null +++ b/server/worker/dispatch.go @@ -0,0 +1,46 @@ +package worker + +// http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html + +type Dispatch struct { + requests chan *Request + workers chan chan *Request + quit chan bool +} + +func NewDispatch(requests chan *Request, workers chan chan *Request) *Dispatch { + return &Dispatch{ + requests: requests, + workers: workers, + quit: make(chan bool), + } +} + +// Start tells the dispatcher to start listening +// for work requests and dispatching to workers. +func (d *Dispatch) Start() { + go func() { + for { + select { + // pickup a request from the queue + case request := <-d.requests: + go func() { + // find an available worker and + // send the request to that worker + worker := <-d.workers + worker <- request + }() + // listen for a signal to exit + case <-d.quit: + return + } + } + }() + +} + +// Stop tells the dispatcher to stop listening for new +// work requests. +func (d *Dispatch) Stop() { + go func() { d.quit <- true }() +} diff --git a/server/worker/request.go b/server/worker/request.go new file mode 100644 index 00000000..e322e302 --- /dev/null +++ b/server/worker/request.go @@ -0,0 +1,12 @@ +package worker + +import ( + "github.com/drone/drone/shared/model" +) + +type Request struct { + User *model.User + Repo *model.Repo + Commit *model.Commit + server *model.Server +} diff --git a/server/worker/worker.go b/server/worker/worker.go new file mode 100644 index 00000000..adadc4ef --- /dev/null +++ b/server/worker/worker.go @@ -0,0 +1,160 @@ +package worker + +import ( + "path/filepath" + "time" + + "github.com/drone/drone/server/database" + "github.com/drone/drone/server/pubsub" + "github.com/drone/drone/shared/build" + "github.com/drone/drone/shared/build/docker" + "github.com/drone/drone/shared/build/git" + "github.com/drone/drone/shared/build/repo" + "github.com/drone/drone/shared/build/script" + "github.com/drone/drone/shared/model" +) + +type Worker interface { + Start() // Start instructs the worker to start processing requests + Stop() // Stop instructions the worker to stop processing requests +} + +type worker struct { + users database.UserManager + repos database.RepoManager + commits database.CommitManager + config database.ConfigManager + pubsub *pubsub.PubSub + server *model.Server + + request chan *Request + dispatch chan chan *Request + quit chan bool +} + +func NewWorker(dispatch chan chan *Request, users database.UserManager, repos database.RepoManager, commits database.CommitManager, config database.ConfigManager, pubsub *pubsub.PubSub, server *model.Server) Worker { + return &worker{ + users: users, + repos: repos, + commits: commits, + config: config, + pubsub: pubsub, + server: server, + dispatch: dispatch, + request: make(chan *Request), + quit: make(chan bool), + } +} + +// Start tells the worker to start listening and +// accepting new work requests. +func (w *worker) Start() { + go func() { + for { + // register our queue with the dispatch + // queue to start accepting work. + go func() { w.dispatch <- w.request }() + + select { + case r := <-w.request: + // handle the request + r.server = w.server + w.Execute(r) + + case <-w.quit: + return + } + } + }() +} + +// Stop tells the worker to stop listening for new +// work requests. +func (w *worker) Stop() { + go func() { w.quit <- true }() +} + +// Execute executes the work Request, persists the +// results to the database, and sends event messages +// to the pubsub (for websocket updates on the website). +func (w *worker) Execute(r *Request) { + // mark the build as Started and update the database + r.Commit.Status = model.StatusStarted + r.Commit.Started = time.Now().Unix() + w.commits.Update(r.Commit) + + // notify all listeners that the build is started + commitc := w.pubsub.Register("_") + commitc.Publish(r) + stdoutc := w.pubsub.Register(r.Commit.ID) + defer stdoutc.Close() + + // create a special buffer that will also + // write to a websocket channel + buf := pubsub.NewBuffer(stdoutc) + + // parse the parameters and build script. The script has already + // been parsed in the hook, so we can be confident it will succeed. + // that being said, we should clean this up + params, _ := r.Repo.ParamMap() + script, _ := script.ParseBuild(r.Commit.Config, params) + + path := r.Repo.Host + "/" + r.Repo.Owner + "/" + r.Repo.Name + repo := &repo.Repo{ + Name: path, + Path: r.Repo.CloneURL, + Branch: r.Commit.Branch, + Commit: r.Commit.Sha, + PR: r.Commit.PullRequest, + Dir: filepath.Join("/var/cache/drone/src", git.GitPath(script.Git, path)), + Depth: git.GitDepth(script.Git), + } + + // create an instance of the Docker builder + builder := build.New(docker.NewHost(w.server.Host)) + builder.Build = script + builder.Repo = repo + builder.Stdout = buf + builder.Key = []byte(r.Repo.PrivateKey) + builder.Timeout = time.Duration(r.Repo.Timeout) * time.Minute + builder.Privileged = r.Repo.Privileged + + //err := builder.Run() + var err error + buf.WriteString("THIS\n") + time.Sleep(3 * time.Second) + buf.WriteString("IS\n") + time.Sleep(3 * time.Second) + buf.WriteString("A\n") + time.Sleep(3 * time.Second) + buf.WriteString("TEST\n") + time.Sleep(3 * time.Second) + + // update the build status based on the results + // from the build runner. + switch { + case err != nil: + r.Commit.Status = model.StatusError + buf.WriteString(err.Error()) + case builder.BuildState == nil: + r.Commit.Status = model.StatusFailure + case builder.BuildState.ExitCode != 0: + r.Commit.Status = model.StatusFailure + default: + r.Commit.Status = model.StatusSuccess + } + + // calcualte the build finished and duration details and + // update the commit + r.Commit.Finished = time.Now().Unix() + r.Commit.Duration = (r.Commit.Finished - r.Commit.Started) + w.commits.Update(r.Commit) + w.commits.UpdateOutput(r.Commit, buf.Bytes()) + + // notify all listeners that the build is finished + commitc.Publish(r) + + // todo(bradrydzewski) update github status API + // todo(bradrydzewski) send email notifications + // todo(bradrydzewski) send other notifications +} diff --git a/shared/build/docker/client.go b/shared/build/docker/client.go index a02300a7..5dc803da 100644 --- a/shared/build/docker/client.go +++ b/shared/build/docker/client.go @@ -40,6 +40,23 @@ func New() *Client { return c } +func NewHost(address string) *Client { + c := &Client{} + + // parse the address and split + pieces := strings.Split(address, "://") + if len(pieces) == 2 { + c.proto = pieces[0] + c.addr = pieces[1] + } else if len(pieces) == 1 { + c.addr = pieces[0] + } + + c.Images = &ImageService{c} + c.Containers = &ContainerService{c} + return c +} + type Client struct { proto string addr string diff --git a/shared/model/remote.go b/shared/model/remote.go new file mode 100644 index 00000000..74f6ca2c --- /dev/null +++ b/shared/model/remote.go @@ -0,0 +1,20 @@ +package model + +const ( + RemoteGithub = "github.com" + RemoteGitlab = "gitlab.com" + RemoteGithubEnterprise = "enterprise.github.com" + RemoteBitbucket = "bitbucket.org" + RemoteStash = "stash.atlassian.com" +) + +type Remote struct { + ID int64 `meddler:"remote_id,pk" json:"id"` + Type string `meddler:"remote_type" json:"type"` + Host string `meddler:"remote_host" json:"host"` + URL string `meddler:"remote_url" json:"url"` + API string `meddler:"remote_api" json:"api"` + Client string `meddler:"remote_client" json:"client"` + Secret string `meddler:"remote_secret" json:"secret"` + Open bool `meddler:"remote_open" json:"open"` +} diff --git a/shared/model/server.go b/shared/model/server.go new file mode 100644 index 00000000..c55e8a69 --- /dev/null +++ b/shared/model/server.go @@ -0,0 +1,19 @@ +package model + +type Server struct { + ID int64 `meddler:"server_id,pk" json:"id"` + Name string `meddler:"server_name" json:"name"` + Host string `meddler:"server_host" json:"host"` + User string `meddler:"server_user" json:"user"` + Pass string `meddler:"server_pass" json:"name"` + Cert string `meddler:"server_cert" json:"cert"` +} + +type SMTPServer struct { + ID int64 `meddler:"smtp_id,pk" json:"id"` + From string `meddler:"smtp_from" json:"from"` + Host string `meddler:"smtp_host" json:"host"` + Port string `meddler:"smtp_host" json:"port"` + User string `meddler:"smtp_user" json:"user"` + Pass string `meddler:"smtp_pass" json:"name"` +}