diff --git a/Makefile b/Makefile
index 1a06482..903b7a3 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ release: clean deps ## Generate releases for unix systems
do \
echo "Building $$os-$$arch"; \
mkdir -p build/webhook-$$os-$$arch/; \
- GOOS=$$os GOARCH=$$arch go build -o build/webhook-$$os-$$arch/webhook; \
+ CGO_ENABLED=0 GOOS=$$os GOARCH=$$arch go build -o build/webhook-$$os-$$arch/webhook; \
tar cz -C build -f build/webhook-$$os-$$arch.tar.gz webhook-$$os-$$arch; \
done \
done
diff --git a/README.md b/README.md
index c9b43a5..20dd6a8 100644
--- a/README.md
+++ b/README.md
@@ -18,9 +18,9 @@ If you use Mattermost or Slack, you can set up an "Outgoing webhook integration"
Everything else is the responsibility of the command's author.
## Not what you're looking for?
-| | |
+| | |
| :-: | :-: |
-| Scriptable webhook gateway to safely run your custom builds, deploys, and proxy scripts on your servers. | Inspect, monitor and replay webhooks without the back and forth troubleshooting. |
+| Scriptable webhook gateway to safely run your custom builds, deploys, and proxy scripts on your servers. | An event gateway to reliably ingest, verify, queue, transform, filter, inspect, monitor, and replay webhooks. |
# Getting started
@@ -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)
- [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
- - [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)
- [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/)
diff --git a/docs/Hook-Examples.md b/docs/Hook-Examples.md
index 7d07932..66aa5a7 100644
--- a/docs/Hook-Examples.md
+++ b/docs/Hook-Examples.md
@@ -22,6 +22,25 @@ although the examples on this page all use the JSON format.
* [Multipart Form Data](#multipart-form-data)
* [Pass string arguments to command](#pass-string-arguments-to-command)
* [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
@@ -213,7 +232,11 @@ Values in the request body can be accessed in the command or to the match rule b
}
]
```
+
## Incoming Gitea webhook
+
+JSON version:
+
```json
[
{
@@ -228,7 +251,7 @@ Values in the request body can be accessed in the command or to the match rule b
},
{
"source": "payload",
- "name": "pusher.name"
+ "name": "pusher.full_name"
},
{
"source": "payload",
@@ -242,12 +265,12 @@ Values in the request body can be accessed in the command or to the match rule b
{
"match":
{
- "type": "value",
- "value": "mysecret",
+ "type": "payload-hmac-sha256",
+ "secret": "mysecret",
"parameter":
{
- "source": "payload",
- "name": "secret"
+ "source": "header",
+ "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":
{
"type": "value",
- "value": "refs/heads/master",
+ "value": "refs/heads/main",
"parameter":
{
"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
```json
[
@@ -498,7 +550,8 @@ A reference to the second item in the array would look like this:
[
{
"id": "sendgrid",
- "execute-command": "{{ .Hookecho }}",
+ "execute-command": "/root/my-server/deployment.sh",
+ "command-working-directory": "/root/my-server",
"trigger-rule": {
"match": {
"type": "value",
@@ -672,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"
+ }
+ }
+ }
+ ]
+ }
+ }
+]
+```
diff --git a/docs/Templates.md b/docs/Templates.md
index 35f10a0..3355112 100644
--- a/docs/Templates.md
+++ b/docs/Templates.md
@@ -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
[tt]: https://golang.org/pkg/text/template/
diff --git a/images/hookdeck-black.svg b/images/hookdeck-black.svg
new file mode 100644
index 0000000..962b07d
--- /dev/null
+++ b/images/hookdeck-black.svg
@@ -0,0 +1,11 @@
+
diff --git a/images/hookdeck-white.svg b/images/hookdeck-white.svg
new file mode 100644
index 0000000..19afbbf
--- /dev/null
+++ b/images/hookdeck-white.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/internal/hook/hook.go b/internal/hook/hook.go
index 6699eeb..394dd79 100644
--- a/internal/hook/hook.go
+++ b/internal/hook/hook.go
@@ -13,12 +13,12 @@ import (
"errors"
"fmt"
"hash"
- "io/ioutil"
"log"
"math"
"net"
"net/textproto"
"os"
+ "path"
"reflect"
"regexp"
"strconv"
@@ -750,14 +750,18 @@ func (h *Hooks) LoadFromFile(path string, asTemplate bool) error {
}
// parse hook file for hooks
- file, e := ioutil.ReadFile(path)
+ file, e := os.ReadFile(path)
if e != nil {
return e
}
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))
if err != nil {
@@ -956,3 +960,27 @@ func compare(a, b string) bool {
func getenv(s string) string {
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))
+}