153 lines
4.5 KiB
Plaintext
153 lines
4.5 KiB
Plaintext
|
lapis = require "lapis"
|
||
|
config = require("lapis.config").get!
|
||
|
|
||
|
import respond_to, json_params from require "lapis.application"
|
||
|
import hmac_sha1, hmac_sha256 from require "lapis.util.encoding"
|
||
|
import encode from require "cjson"
|
||
|
import GithookLogs from require "models"
|
||
|
import locate, autoload, registry from require "locator"
|
||
|
import settings from autoload "utility"
|
||
|
import execute from locate "utility.shell"
|
||
|
import insert, concat from table
|
||
|
|
||
|
const_compare = (string1, string2) ->
|
||
|
local fail, dummy
|
||
|
|
||
|
for i = 1, math.max #string1, #string2, 100
|
||
|
if string1\sub(i,i) ~= string2\sub(i,i)
|
||
|
fail = true
|
||
|
else
|
||
|
dummy = true -- attempting to make execution time equal
|
||
|
|
||
|
return not fail
|
||
|
|
||
|
hex_dump = (str) ->
|
||
|
len = string.len str
|
||
|
hex = ""
|
||
|
|
||
|
for i = 1, len
|
||
|
hex ..= string.format( "%02x", string.byte( str, i ) )
|
||
|
|
||
|
return hex
|
||
|
|
||
|
run_update = (branch) ->
|
||
|
exit_codes, logs = {}, {}
|
||
|
failure = false
|
||
|
|
||
|
commands = registry.githook_commands branch, config._name
|
||
|
unless commands
|
||
|
commands = {
|
||
|
{"git checkout #{branch} 2> /dev/stdout"}
|
||
|
{"git pull origin 2> /dev/stdout"}
|
||
|
{"git submodule init 2> /dev/stdout"}
|
||
|
{"git submodule update 2> /dev/stdout"}
|
||
|
{"code=0\nfor file in $(find . -type f -name \"*.moon\"); do moonc \"$file\" 2> /dev/stdout\ntmp=$?\nif [ ! $tmp -eq 0 ]; then code=$tmp\nfi; done\necho $code", false}
|
||
|
{"lapis migrate #{config._name} 2> /dev/stdout"}
|
||
|
{"lapis build #{config._name} 2> /dev/stdout"}
|
||
|
}
|
||
|
for cmd in *commands
|
||
|
code, output = execute unpack cmd
|
||
|
insert exit_codes, code
|
||
|
insert logs, cmd[1]
|
||
|
insert logs, " #{output\gsub "\n", "\n "}"
|
||
|
if code != 0
|
||
|
failure = true
|
||
|
break
|
||
|
|
||
|
log = concat logs, "\n"
|
||
|
|
||
|
if failure
|
||
|
if settings["githook.save_logs"]
|
||
|
GithookLogs\create {
|
||
|
success: false
|
||
|
exit_codes: encode exit_codes
|
||
|
:log
|
||
|
}
|
||
|
return status: 500, json: {
|
||
|
status: "failure"
|
||
|
message: "a subprocess returned a non-zero exit code"
|
||
|
:log
|
||
|
:exit_codes
|
||
|
}
|
||
|
else
|
||
|
if settings["githook.save_logs"] and settings["githook.save_on_success"]
|
||
|
GithookLogs\create {
|
||
|
exit_codes: encode exit_codes
|
||
|
:log
|
||
|
}
|
||
|
elseif settings["githook.save_logs"]
|
||
|
GithookLogs\create! -- we still record WHEN there was a success
|
||
|
return status: 200, json: {
|
||
|
status: "success"
|
||
|
message: "server updated to latest version of '#{branch}'"
|
||
|
:log
|
||
|
:exit_codes
|
||
|
}
|
||
|
|
||
|
ignored = (branch) ->
|
||
|
return status: 200, json: {
|
||
|
status: "success"
|
||
|
message: "ignored push (looking for updates to '#{branch}')"
|
||
|
}
|
||
|
|
||
|
unauthorized = ->
|
||
|
return status: 401, json: {
|
||
|
status: "unauthorized",
|
||
|
message: "invalid credentials or no credentials were sent"
|
||
|
}
|
||
|
|
||
|
invalid = (reason) ->
|
||
|
return status: 400, json: {
|
||
|
status: "invalid request"
|
||
|
message: reason
|
||
|
}
|
||
|
|
||
|
class extends lapis.Application
|
||
|
[githook: "/githook"]: respond_to {
|
||
|
before: =>
|
||
|
@branch = config.githook_branch or settings["githook.branch"] or "master"
|
||
|
|
||
|
GET: =>
|
||
|
unless settings["githook.allow_get"]
|
||
|
return status: 405, json: {
|
||
|
status: "method not allowed",
|
||
|
message: "Githook is not accepting GET requests."
|
||
|
}
|
||
|
|
||
|
unless settings["githook.run_without_auth"]
|
||
|
return unauthorized!
|
||
|
|
||
|
@results = run_update(@branch)
|
||
|
return render: locate "views.githook_get"
|
||
|
|
||
|
POST: json_params =>
|
||
|
secret = config.githook_secret or settings["githook.secret"]
|
||
|
if secret
|
||
|
ngx.req.read_body!
|
||
|
if body = ngx.req.get_body_data!
|
||
|
local authorized
|
||
|
if github_hash = @req.headers["X-Hub-Signature"]
|
||
|
authorized = const_compare "sha1=#{hex_dump hmac_sha1 secret, body}", github_hash
|
||
|
elseif gogs_hash = @req.headers["X-Gogs-Signature"]
|
||
|
authorized = const_compare gogs_hash, hex_dump hmac_sha256 secret, body
|
||
|
elseif @params.secret
|
||
|
authorized = const_compare @params.secret, secret
|
||
|
unless authorized
|
||
|
return unauthorized!
|
||
|
if @params.ref == "refs/heads/#{@branch}"
|
||
|
return run_update(@branch)
|
||
|
elseif @params.ref == nil
|
||
|
return invalid "'ref' not defined in request body"
|
||
|
else
|
||
|
return ignored(@branch)
|
||
|
else
|
||
|
return invalid "no request body"
|
||
|
elseif settings["githook.run_without_auth"]
|
||
|
if @params.ref == "refs/heads/#{@branch}"
|
||
|
return run_update(@branch)
|
||
|
else
|
||
|
return ignored(@branch)
|
||
|
else
|
||
|
return unauthorized!
|
||
|
}
|