mirror of
https://github.com/adnanh/webhook.git
synced 2026-07-28 01:59:17 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f78eeeca7d | |||
| 8a2d3f85cd | |||
| 34d4418840 | |||
| 7ea2a53bbb | |||
| 2da9957e86 | |||
| 5981da2717 | |||
| 1b13355196 | |||
| eb7e8f5ba8 |
@@ -141,7 +141,7 @@ Check out [Hook examples page](docs/Hook-Examples.md) for more complex examples
|
|||||||
- [Using Prometheus to Automatically Scale WebLogic Clusters on Kubernetes](https://blogs.oracle.com/weblogicserver/using-prometheus-to-automatically-scale-weblogic-clusters-on-kubernetes-v5) by [Marina Kogan](https://blogs.oracle.com/author/9a4fe754-1cc2-4c64-95fc-360642b62927)
|
- [Using Prometheus to Automatically Scale WebLogic Clusters on Kubernetes](https://blogs.oracle.com/weblogicserver/using-prometheus-to-automatically-scale-weblogic-clusters-on-kubernetes-v5) by [Marina Kogan](https://blogs.oracle.com/author/9a4fe754-1cc2-4c64-95fc-360642b62927)
|
||||||
- [Github Pages and Jekyll - A New Platform for LACNIC Labs](https://labs.lacnic.net/a-new-platform-for-lacniclabs/) by [Carlos Martínez Cagnazzo](https://twitter.com/carlosm3011)
|
- [Github Pages and Jekyll - A New Platform for LACNIC Labs](https://labs.lacnic.net/a-new-platform-for-lacniclabs/) by [Carlos Martínez Cagnazzo](https://twitter.com/carlosm3011)
|
||||||
- [How to Deploy React Apps Using Webhooks and Integrating Slack on Ubuntu](https://www.alibabacloud.com/blog/how-to-deploy-react-apps-using-webhooks-and-integrating-slack-on-ubuntu_594116) by Arslan Ud Din Shafiq
|
- [How to Deploy React Apps Using Webhooks and Integrating Slack on Ubuntu](https://www.alibabacloud.com/blog/how-to-deploy-react-apps-using-webhooks-and-integrating-slack-on-ubuntu_594116) by Arslan Ud Din Shafiq
|
||||||
- [Private webhooks](https://ihateithe.re/2018/01/private-webhooks/) by [Thomas](https://ihateithe.re/colophon/)
|
- [Private webhooks](https://tmertz.com/2018/01/private-webhooks/) by [Thomas](https://tmertz.com)
|
||||||
- [Adventures in webhooks](https://medium.com/@draketech/adventures-in-webhooks-2d6584501c62) by [Drake](https://medium.com/@draketech)
|
- [Adventures in webhooks](https://medium.com/@draketech/adventures-in-webhooks-2d6584501c62) by [Drake](https://medium.com/@draketech)
|
||||||
- [GitHub pro tips](http://notes.spencerlyon.com/2016/01/04/github-pro-tips/) by [Spencer Lyon](http://notes.spencerlyon.com/)
|
- [GitHub pro tips](http://notes.spencerlyon.com/2016/01/04/github-pro-tips/) by [Spencer Lyon](http://notes.spencerlyon.com/)
|
||||||
- [XiaoMi Vacuum + Amazon Button = Dash Cleaning](https://www.instructables.com/id/XiaoMi-Vacuum-Amazon-Button-Dash-Cleaning/) by [c0mmensal](https://www.instructables.com/member/c0mmensal/)
|
- [XiaoMi Vacuum + Amazon Button = Dash Cleaning](https://www.instructables.com/id/XiaoMi-Vacuum-Amazon-Button-Dash-Cleaning/) by [c0mmensal](https://www.instructables.com/member/c0mmensal/)
|
||||||
|
|||||||
+127
-6
@@ -22,6 +22,25 @@ although the examples on this page all use the JSON format.
|
|||||||
* [Multipart Form Data](#multipart-form-data)
|
* [Multipart Form Data](#multipart-form-data)
|
||||||
* [Pass string arguments to command](#pass-string-arguments-to-command)
|
* [Pass string arguments to command](#pass-string-arguments-to-command)
|
||||||
* [Receive Synology DSM notifications](#receive-synology-notifications)
|
* [Receive Synology DSM notifications](#receive-synology-notifications)
|
||||||
|
* [Incoming Azure Container Registry (ACR) webhook](#incoming-acr-webhook)
|
||||||
|
|
||||||
|
## Printing the Raw Webhook Payload to Standard Output
|
||||||
|
|
||||||
|
This hook configuration receives incoming webhook requests and prints the raw request body (payload) directly to the server's standard output (visible in the webhook process logs when running with -verbose). It is particularly useful for debugging and verifying webhook deliveries from external services.
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "print-payload",
|
||||||
|
"execute-command": "/bin/echo",
|
||||||
|
"pass-arguments-to-command": [
|
||||||
|
{
|
||||||
|
"source": "entire-payload",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
## Incoming Github webhook
|
## Incoming Github webhook
|
||||||
|
|
||||||
@@ -213,7 +232,11 @@ Values in the request body can be accessed in the command or to the match rule b
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Incoming Gitea webhook
|
## Incoming Gitea webhook
|
||||||
|
|
||||||
|
JSON version:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@@ -228,7 +251,7 @@ Values in the request body can be accessed in the command or to the match rule b
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "payload",
|
"source": "payload",
|
||||||
"name": "pusher.name"
|
"name": "pusher.full_name"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"source": "payload",
|
"source": "payload",
|
||||||
@@ -242,12 +265,12 @@ Values in the request body can be accessed in the command or to the match rule b
|
|||||||
{
|
{
|
||||||
"match":
|
"match":
|
||||||
{
|
{
|
||||||
"type": "value",
|
"type": "payload-hmac-sha256",
|
||||||
"value": "mysecret",
|
"secret": "mysecret",
|
||||||
"parameter":
|
"parameter":
|
||||||
{
|
{
|
||||||
"source": "payload",
|
"source": "header",
|
||||||
"name": "secret"
|
"name": "X-Gitea-Signature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -255,7 +278,7 @@ Values in the request body can be accessed in the command or to the match rule b
|
|||||||
"match":
|
"match":
|
||||||
{
|
{
|
||||||
"type": "value",
|
"type": "value",
|
||||||
"value": "refs/heads/master",
|
"value": "refs/heads/main",
|
||||||
"parameter":
|
"parameter":
|
||||||
{
|
{
|
||||||
"source": "payload",
|
"source": "payload",
|
||||||
@@ -269,6 +292,35 @@ Values in the request body can be accessed in the command or to the match rule b
|
|||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
YAML version:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- id: webhook
|
||||||
|
execute-command: /home/adnan/redeploy-go-webhook.sh
|
||||||
|
command-working-directory: /home/adnan/go
|
||||||
|
pass-arguments-to-command:
|
||||||
|
- source: payload
|
||||||
|
name: head_commit.id
|
||||||
|
- source: payload
|
||||||
|
name: pusher.full_name
|
||||||
|
- source: payload
|
||||||
|
name: pusher.email
|
||||||
|
trigger-rule:
|
||||||
|
and:
|
||||||
|
- match:
|
||||||
|
type: payload-hmac-sha256
|
||||||
|
secret: mysecret
|
||||||
|
parameter:
|
||||||
|
source: header
|
||||||
|
name: X-Gitea-Signature
|
||||||
|
- match:
|
||||||
|
type: value
|
||||||
|
value: refs/heads/main
|
||||||
|
parameter:
|
||||||
|
source: payload
|
||||||
|
name: ref
|
||||||
|
```
|
||||||
|
|
||||||
## Slack slash command
|
## Slack slash command
|
||||||
```json
|
```json
|
||||||
[
|
[
|
||||||
@@ -673,3 +725,72 @@ Webhooks feature introduced in DSM 7.x seems to be incomplete & broken, but you
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
## Incoming Azure Container Registry (ACR) webhook
|
||||||
|
|
||||||
|
ACR can send webhooks on image push events. The `hooks.json` below will handle those events and pass relevant properties as environment variables to a command.
|
||||||
|
|
||||||
|
Here is an example of a working docker webhook container used to handle the webhooks and fill the cache of a local registry: [ACR Harbor local cache feeder](https://github.com/tomdess/registry-cache-feeder).
|
||||||
|
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "acr-push-event",
|
||||||
|
"execute-command": "/config/script-acr.sh",
|
||||||
|
"command-working-directory": "/config",
|
||||||
|
"pass-environment-to-command":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"envname": "ACTION",
|
||||||
|
"source": "payload",
|
||||||
|
"name": "action"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"envname": "REPO",
|
||||||
|
"source": "payload",
|
||||||
|
"name": "target.repository"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"envname": "TAG",
|
||||||
|
"source": "payload",
|
||||||
|
"name": "target.tag"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"envname": "DIGEST",
|
||||||
|
"source": "payload",
|
||||||
|
"name": "target.digest"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"trigger-rule":
|
||||||
|
{
|
||||||
|
"and":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"match":
|
||||||
|
{
|
||||||
|
"type": "value",
|
||||||
|
"value": "mysecretToken",
|
||||||
|
"parameter":
|
||||||
|
{
|
||||||
|
"source": "header",
|
||||||
|
"name": "X-Static-Token"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"match":
|
||||||
|
{
|
||||||
|
"type": "value",
|
||||||
|
"value": "push",
|
||||||
|
"parameter":
|
||||||
|
{
|
||||||
|
"source": "payload",
|
||||||
|
"name": "action"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|||||||
@@ -73,5 +73,38 @@ Additionally, the result is piped through the built-in Go template function `js`
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Template Functions
|
||||||
|
|
||||||
|
In addition to the [built-in Go template functions and features][tt], `webhook` provides the following functions:
|
||||||
|
|
||||||
|
### `getenv`
|
||||||
|
|
||||||
|
The `getenv` template function can be used for inserting environment variables into a templated configuration file.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
"Secret": "{{getenv TEST_secret | js}}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `cat`
|
||||||
|
|
||||||
|
The `cat` template function can be used to read a file from the local filesystem. This is useful for reading secrets from files. If the file doesn't exist, it returns an empty string.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
"secret": "{{ cat "/run/secrets/my-secret" | js }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
### `credential`
|
||||||
|
|
||||||
|
The `credential` template function provides a way to retrieve secrets using [systemd's LoadCredential mechanism](https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Credentials). It reads the file specified by the given name from the directory specified in the `CREDENTIALS_DIRECTORY` environment variable.
|
||||||
|
|
||||||
|
If `CREDENTIALS_DIRECTORY` is not set, it will fall back to using `getenv` to read the secret from an environment variable of the given name.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```
|
||||||
|
"secret": "{{ credential "my-secret" | js }}"
|
||||||
|
```
|
||||||
|
|
||||||
[w]: https://github.com/adnanh/webhook
|
[w]: https://github.com/adnanh/webhook
|
||||||
[tt]: https://golang.org/pkg/text/template/
|
[tt]: https://golang.org/pkg/text/template/
|
||||||
|
|||||||
+31
-3
@@ -13,12 +13,12 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -750,14 +750,18 @@ func (h *Hooks) LoadFromFile(path string, asTemplate bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse hook file for hooks
|
// parse hook file for hooks
|
||||||
file, e := ioutil.ReadFile(path)
|
file, e := os.ReadFile(path)
|
||||||
|
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
if asTemplate {
|
if asTemplate {
|
||||||
funcMap := template.FuncMap{"getenv": getenv}
|
funcMap := template.FuncMap{
|
||||||
|
"cat": cat,
|
||||||
|
"credential": credential,
|
||||||
|
"getenv": getenv,
|
||||||
|
}
|
||||||
|
|
||||||
tmpl, err := template.New("hooks").Funcs(funcMap).Parse(string(file))
|
tmpl, err := template.New("hooks").Funcs(funcMap).Parse(string(file))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -956,3 +960,27 @@ func compare(a, b string) bool {
|
|||||||
func getenv(s string) string {
|
func getenv(s string) string {
|
||||||
return os.Getenv(s)
|
return os.Getenv(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cat provides a template function to retrieve content of files
|
||||||
|
// Similarly to getenv, if no file is found, it returns the empty string
|
||||||
|
func cat(s string) string {
|
||||||
|
data, e := os.ReadFile(s)
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSuffix(string(data), "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// credential provides a template function to retreive secrets using systemd's LoadCredential mechanism
|
||||||
|
func credential(s string) string {
|
||||||
|
dir := getenv("CREDENTIALS_DIRECTORY")
|
||||||
|
|
||||||
|
// If no credential directory is found, fallback to the env variable
|
||||||
|
if dir == "" {
|
||||||
|
return getenv(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cat(path.Join(dir, s))
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
version = "2.8.2"
|
version = "2.8.3"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
Reference in New Issue
Block a user