webhook-builder

Crates.iowebhook-builder
lib.rswebhook-builder
version0.1.0
created_at2026-01-01 20:58:38.972422+00
updated_at2026-01-01 20:58:38.972422+00
descriptionRun a script when a git forge webhook is triggered, for basic CI/CD
homepage
repositoryhttps://git.filmroellchen.eu/filmroellchen/webhook-builder
max_upload_size
id2017505
size63,400
kleines Filmröllchen (kleinesfilmroellchen)

documentation

README

webhook-builder

Silly little webserver that runs some command when you POST to it. Point a git forge’s webhook at it and enjoy your “continuous deployment”. Known to work with Forgejo Webhooks (which are exactly the same as Gitea and Gogs and almost the same as GitHub).

This could be a shell script in itself but I couldn’t find any reasonable way of doing that which didn’t involve http.sh. So instead I wrote 200 lines of Rust. (Also, there’s way too many dependencies here. I’m sorry, but actix is the thing that works so I use it. Write your better replacement please.)

Installation

It’s Rust. cargo build and/or cargo install. If you want this on crates.io, scream at me I guess.

Usage

Note that the script UX is super minimal, just enough to know what’s going on for me. Likely won’t be improved.

The script takes a single argument, the path to the config file. Config format is really simple KDL.

// Bind to any endpoint you want. There is no default.
// Can be used as often as you like.
bind "[::1]:8111"

// The necessary Authorization HTTP header the other end needs to send in order for the action to actually be executed.
// THIS IS A CRITICAL SECURITY FEATURE. Setting this to an empty value or to something predictable (or leaving the default) WILL get you pwned.
// On most forges, you can either configure this setting as you want (in that case, use a long random string), or they will pre-configure some value for you that you can find out somehow.
auth_header "Basic DEFINITELY-CHANGE-ME-YOU-WILL-GET-PWNED-IF-YOU-KEEP-THIS"

// Action to execute when the webhook is called. By default the executable is `sh -c` (`sh` as per $PATH), so you can easily specify any shell script you want.
// You can instead use the `executable` property to set a different executable (this can also be located in $PATH).
// The executable will start out in the same directory as the webserver, with the same user and umask. All environment variables from the server are passed through.
// The arguments are all passed to the executable as-is.
action #"""
       cd $HOME
       echo 'hi from the hook script!'
       """#

// Example for using `ls` directly.
// For demonstration purposes only; multiple `action` options are not allowed.
action executable="ls" "-l" "-a" "/"

The webserver only receives POST and GET requests on the root path. If the correct authorization header is passed (see config above), it runs the action script. It ignores literally everything else in the request, including all the git-related information in the body of POST requests. It returns 202 if the child could be spawned, even if it later returns a non-zero exit code. Since stdout/stderr are passed through, you should be able to debug this by looking at the server logs. The child process may take much longer than most servers accept for an HTTP response, which is why the server doesn’t wait for the child to complete before sending a response. 500 is returned if anything with spawning the child process fails.

Because there only ever should be one webhook being posted at this service, it only runs one web worker and a single-threaded tokio runtime. (If we only could remove tokio entirely.)

Contributing

Don’t.

I really don’t care about anything here. I might accept your PR if it doesn’t make the thing worse for me.

License

As all garbage should be, this garbage is public domain under the Unlicense.

Commit count: 0

cargo fmt