Merge commit '1968f5e88ed0cc59f5db07be1d162b12120d7f96' as 'applications/githook'

This commit is contained in:
Paul Liverman III 2018-05-11 13:51:27 -07:00
commit 73ce07dc01
7 changed files with 268 additions and 0 deletions

1
applications/githook/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.lua

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Paul Liverman III
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,55 @@
## Installation
(Note: I'm going to rewrite this to explain how to use with locator, a simple
server locator I designed for use with Lapis and these sub-applications.)
Dependencies:
- Lapis (duh)
- MoonScript
- OpenResty user needs a bash shell (ch -s /bin/bash user)
From the shell:
```bash
git subtree add --prefix githook https://github.com/lazuscripts/githook.git master --squash
```
(`--prefix` specifies where it will be saved.)
Alternately, you can add it as a remote for easier maintenance:
```bash
git remote add -f githook https://github.com/lazuscripts/githook.git
git subtree add --prefix githook githook master --squash
```
From your main application class: `@include "githook.githook"` (or wherever you put it)
### Updating
From the shell:
```bash
git subtree pull --prefix githook https://github.com/lazuscripts/githook.git master --squash
```
Or, if it is set up as remote:
```bash
git subtree pull --prefix githook githook master --squash
```
## Config
All configuration is optional. Without configuration, will attempt to update any
time it is visited.
- `githook_branch "branch"` which branch you want updating (as string)
(to prevent updates triggering when pushing unrelated branches)
- `githook_secret "secret"` the secret string used on GitHub
Will attempt to checkout, pull, update submodules if needed, compile all code,
then run migrations, and finally update the running server without interruption.
Returns a log along with exit codes on success or failure.

View File

@ -0,0 +1,152 @@
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!
}

View File

@ -0,0 +1,25 @@
import create_table, types, create_index from require "lapis.db.schema"
import autoload from require "locator"
import settings from autoload "utility"
{
[1519992142]: =>
create_table "githook_logs", {
{"id", types.serial primary_key: true}
{"success", types.boolean default: true}
{"exit_codes", types.text null: true}
{"log", types.text null: true}
{"created_at", types.time}
{"updated_at", types.time}
}
create_index "githook_logs", "id", unique: true
create_index "githook_logs", "success"
settings["githook.save_logs"] = true
settings["githook.save_on_success"] = true
settings["githook.allow_get"] = true
settings["githook.run_without_auth"] = false
-- settings["githook.branch"] = "master"
settings.save!
}

View File

@ -0,0 +1,4 @@
import Model from require "lapis.db.model"
class GithookLogs extends Model
@timestamp: true

View File

@ -0,0 +1,10 @@
import Widget from require "lapis.html"
class extends Widget
content: =>
h2 "#{@results.json.status\sub(1, 1)\upper!}#{@results.json.status\sub 2}"
element "table", ->
tr ->
for code in *@results.json.exit_codes
th code
pre @results.json.log