Allow executed script to have arguments

This commit is contained in:
Akbar Rahman 2021-08-04 11:51:40 +01:00
parent a2d4153c64
commit 2189ee511c
4 changed files with 54 additions and 26 deletions

View File

@ -2,13 +2,16 @@
"ListenAddress": "127.0.0.1:8654", "ListenAddress": "127.0.0.1:8654",
"Services": { "Services": {
"test": { "test": {
"Script": "./example.sh", "Script": {
"Program": "./example.sh",
"AppendPayload": true
},
"Secret": "THISISVERYSECRET", "Secret": "THISISVERYSECRET",
"SignatureHeader": "X-Gitea-Signature", "SignatureHeader": "X-Gitea-Signature",
"Tests": [ "Tests": [
{ {
"Command": "git", "Program": "echo",
"Arguments": [ "pull" ] "Arguments": [ "test" ]
} }
] ]
} }

View File

@ -1,2 +1,3 @@
#!/usr/bin/bash #!/usr/bin/bash
date >> test_output date >> test_output
echo "$1" >> test_output

49
main.go
View File

@ -24,6 +24,14 @@ func main() {
r := mux.NewRouter() r := mux.NewRouter()
r.HandleFunc("/webhooks/{service}", webhookHandler) r.HandleFunc("/webhooks/{service}", webhookHandler)
if p, ok := os.LookupEnv("CONFIG"); ok {
config_filename = p
}
if p, ok := os.LookupEnv("NO_SIGNATURE_CHECK"); ok {
checkSignature = p != "true"
}
raw_config, err := ioutil.ReadFile(config_filename) raw_config, err := ioutil.ReadFile(config_filename)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
@ -34,13 +42,6 @@ func main() {
panic(err.Error()) panic(err.Error())
} }
if p, ok := os.LookupEnv("CONFIG"); ok {
config_filename = p
}
if p, ok := os.LookupEnv("NO_SIGNATURE_CHECK"); ok {
checkSignature = p != "true"
}
log.Fatal(http.ListenAndServe(config.ListenAddress, r)) log.Fatal(http.ListenAndServe(config.ListenAddress, r))
} }
@ -73,15 +74,15 @@ func webhookHandler(w http.ResponseWriter, r *http.Request) {
// Run tests, immediately stop if one fails // Run tests, immediately stop if one fails
for _, test := range service.Tests { for _, test := range service.Tests {
if _, err := exec.Command(test.Command, test.Arguments...).Output(); err != nil { if _, err := test.Execute(payload); err != nil {
writeResponse(w, 409, writeResponse(w, 409,
fmt.Sprintf("409 Conflict: Test failed: %v", err.Error()), fmt.Sprintf("Conflict: Test failed: %v", err.Error()),
) )
return return
} }
} }
if stdout, err := exec.Command(service.Script, payload).Output(); err != nil { if stdout, err := service.Script.Execute(payload); err != nil {
writeResponse(w, 500, err.Error()) writeResponse(w, 500, err.Error())
return return
} else { } else {
@ -106,9 +107,12 @@ func (c Config) Validate() error {
return requiredFieldError{"ListenAddress", ""} return requiredFieldError{"ListenAddress", ""}
} }
jsonbytes, _ := json.MarshalIndent(c, "", " ")
fmt.Println(string(jsonbytes))
for serviceName, service := range c.Services { for serviceName, service := range c.Services {
if service.Script == "" { if service.Script.Program == "" {
return requiredFieldError{"Script", serviceName} return requiredFieldError{"Script.Program", serviceName}
} }
if service.SignatureHeader == "" { if service.SignatureHeader == "" {
return requiredFieldError{"SignatureHeader", serviceName} return requiredFieldError{"SignatureHeader", serviceName}
@ -121,16 +125,27 @@ func (c Config) Validate() error {
return nil return nil
} }
type Test struct { func (c Command) Execute(payload string) ([]byte, error) {
Command string arguments := make([]string, 0)
Arguments []string copy(c.Arguments, arguments)
if c.AppendPayload {
arguments = append(arguments, payload)
}
return exec.Command(c.Program, arguments...).Output()
}
type Command struct {
Program string
Arguments []string
AppendPayload bool
} }
type Service struct { type Service struct {
Script string Script Command
Secret string Secret string
SignatureHeader string SignatureHeader string
Tests []Test Tests []Command
} }
type Config struct { type Config struct {

View File

@ -27,7 +27,13 @@ You should also specify a shared secret in the `Secret` key.
You can disable signature verification altogether by setting environment variable You can disable signature verification altogether by setting environment variable
`NO_SIGNATURE_VERIFICATION` to `true`. `NO_SIGNATURE_VERIFICATION` to `true`.
## Tests ## Writing Commands
gohookr doesn't care what the command is as long as the `Program` is executable.
You can specify extra arguments with the `Arguments` field.
You can ask it to put the payload as the last argument by setting `AppendPayload` to true.
## Writing Tests
gohookr can run test before running your script. gohookr can run test before running your script.
Tests must be in the form of bash scripts. Tests must be in the form of bash scripts.
@ -35,13 +41,13 @@ A non-zero return code is considered a fail and gohookr will run no further test
deploy. deploy.
Tests are run in the order they're listed so any actions that need to be done before Tests are run in the order they're listed so any actions that need to be done before
real tests are run can simply be put before the tests. tests are run can simply be put in this section before the tests.
## Example Config ## Example Config
Required config keys are `ListenAddress` and `Services`. Required config keys are `ListenAddress` and `Services`.
Requried keys per service are `Script`, `Secret`, `SignatureHeader`. Requried keys per service are `Script.Program`, `Secret`, `SignatureHeader`.
An example config file can be found [here](./config.json) but also below: An example config file can be found [here](./config.json) but also below:
@ -50,13 +56,16 @@ An example config file can be found [here](./config.json) but also below:
"ListenAddress": "127.0.0.1:8654", "ListenAddress": "127.0.0.1:8654",
"Services": { "Services": {
"test": { "test": {
"Script": "./example.sh", "Script": {
"Program": "./example.sh",
"AppendPayload": true
},
"Secret": "THISISVERYSECRET", "Secret": "THISISVERYSECRET",
"SignatureHeader": "X-Gitea-Signature", "SignatureHeader": "X-Gitea-Signature",
"Tests": [ "Tests": [
{ {
"Command": "git", "Program": "echo",
"Arguments": [ "pull" ] "Arguments": [ "test" ]
} }
] ]
} }