{"id":15890584,"url":"https://github.com/dimfeld/unwebhook","last_synced_at":"2025-03-17T23:31:50.922Z","repository":{"id":17190242,"uuid":"19957802","full_name":"dimfeld/unwebhook","owner":"dimfeld","description":"Webhook server for Gitlab and Github to run arbitrary commands based on events","archived":false,"fork":false,"pushed_at":"2014-05-27T05:37:11.000Z","size":420,"stargazers_count":17,"open_issues_count":0,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-02-28T05:53:22.299Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimfeld.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-05-19T21:08:24.000Z","updated_at":"2024-02-28T23:34:07.000Z","dependencies_parsed_at":"2022-09-12T14:50:57.515Z","dependency_job_id":null,"html_url":"https://github.com/dimfeld/unwebhook","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimfeld%2Funwebhook","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimfeld%2Funwebhook/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimfeld%2Funwebhook/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimfeld%2Funwebhook/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimfeld","download_url":"https://codeload.github.com/dimfeld/unwebhook/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243891970,"owners_count":20364615,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-06T07:06:52.328Z","updated_at":"2025-03-17T23:31:50.615Z","avatar_url":"https://github.com/dimfeld.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"unwebhook\n=============\n\nNeed some custom functionality in response to repository changes, but don't want to go through the effort of writing a webhook? Unwebhook makes it easy, using a simple configuration format to run any command you want in response to an event.\n\nThis hasn't been used in anything approaching a production environment, but it's pretty simple and light on resources.\n\n## Usage\n\nThe program reads information from a configuration format, described below. Configuration can be sourced from multiple files and directories. \n\nThe first command line argument is the main configuration file, whicn contains hook definitions and other server-wide configuration. Additional configuration files can contain only hooks.\n\nThe main configuration file may also be specified in the `UNWEBHOOK_CONFFILE` environment variable. In this case, all command line arguments are limited to containing only hooks.\n\nIf no configuration files are specified, the program looks for a file with of the format *executable-name*.conf.\n\n```shell\n# Read configuration from unwebhook.conf\n% unwebhook\n\n# Read main configuration from main.cfg, and load hooks from all files in webhook-shell.conf.d\n% unwebhook main.cfg unwebhook.conf.d\n\n# Read main configuration from main.cfg, and load hooks from all files and directories on the command line\n% UNWEBHOOK_CONFFILE=main.cfg unwebhook global-hooks.conf hooks.d\n\n# Override listen address from environment\n% UNWEBHOOK_LISTENADDRESS=:8080 unwebhook\n```\n\n## Configuration\nAll configuration is in the [TOML](https://github.com/mojombo/toml) format. \n\n### Server Configuration\nThese variables are read from the main configuration file and determine server-wide behavior. They may also be overriden by environment variables of the format `UNWEBHOOK_VARIABLENAME`\n\n#### ListenAddress\nThe address and port on which to listen. If none is provided, the default `:80` is used.\n\n```\nListenAddress = \"127.0.0.1:8080\"\n```\n\n#### CommandTimeout \n\nThe maximum time, in seconds, that any single command is allowed to run. The default value is 5.\n\n```\nCommandTimeout = 5\n```\n\n#### AcceptIps\n\nA list of IP addresses and prefixes from which to accept requests. Requests from non-allowed IPs are logged and ignored.\n\nIf not specified, requests are allowed from any IP address.\n\n```\nAcceptIps = [ \"172.17.0.1\", \"192.168.1.0/24\", \"2000::/64\" ]\n```\n\n#### Secret\n\nA string used as a key to calculate an HMAC digest of the request body. Requests that don't have a matching\ndigest will be ignored. Note that Gitlab does not support this feature.\n\nIf specified, this overrides any server-wide secret. If a secret is present in the server-wide configuration, it can be disabled for this hook by setting the hook's secret to \"none\".\n\nThe use of a secret or AcceptIps is highly recommended, since it can protect against malicious data being plugged into your commands.\n\n#### LogDir\nThe directory of the log file. If not given, the default is the current directory. This can also be specified on the command line using the -log_dir command-line option.\n\n```\nLogDir = \"/var/log/unwebhook\"\n```\n\n#### HookPaths\n\nAn optional list of files and directories, from which the server will load hooks. These paths are in addition to any hooks defined in the main configuration file or additional command line arguments.\n\n```\nHookPaths = [ \"/etc/unwebhook/conf.d\", \"/etc/unwebhook/hooks.conf\" ]\n```\n\n#### Hook\nAn optional list of Hook objects. When given in the server configuration file, this looks identical to a list of hooks as described in the Hook Configuration section below. Due to limitations of the TOML format, any hook definitions in the main configuration file must be at the end.\n\n### Hook Configuration\n\n#### Url\nThe path at which the server exposes this hook. This does not include the server name, and must start with a slash.\n\n```\nUrl = \"/webhook/\"\nor\nUrl = \"/webhook/:repo/hook\"\n```\n\nA path element starting with a colon is a wildcard, which will match on any text in the given path element. In the example above, if the URL configured in GitHub is `/webhook/abc/hook`, then the `repo` variable will be set to `abc`. \n\nVariables captured in this way are accessible in commands using the syntax `{{ .urlparams.ElementName }}`. In the example here, `{{ .urlparams.repo }}` would be replaced by the text `abc`. See below for more details on the substitution system.\n\n#### Dir\nThe working directory from which to run the command.\n\n```\nDir = \"/home/user/repos\"\n```\n\n#### Env\nA list environment variables, formatted as `key=value` that should replace the current environment. If not given, the current environment is passed into the commands.\n\n```\nEnv = [ \"GONUMPROCS=1\", \"USER=abc\" ]\n```\n\n#### PerCommit\nIf this value is `true`, the hook will be run once for each commit in a push event, with the current commit exposed in the templating system as `.commit`.  A hook configured like this will not run anything for an event with no commits.\n\nIf the value is `false`, the hook is run once per event.\n\n```\nPerCommit = true\n```\n\n#### AllowEvent \n\nA list of events that this hook is allowed to handle. If an event's type is not in this list, it is ignored.\n\nThis data is drawn from the `X-Github-Event` HTTP header field for Github events, and from the `object-kind` JSON field for Gitlab events. Gitlab events without an `object-kind` are given the value `push`.\n\n```\nAllowEvent = [ \"push\", \"commit_comment\" ]\n```\n\n#### AllowBranches\n\nA list of branches that this hook is allowed to handle.\n\nThis data is drawn from the `ref` field, with the `/refs/heads/` prefix stripped off.\n\n```\nAllowBranches = [ \"master\" ]\n```\n\n#### Secret \n\nA string used as a key to calculate an HMAC digest of the request body. Requests that don't have a matching\ndigest will be ignored. Note that Gitlab does not support this feature.\n\nIf specified, this overrides any secret from the server-wide configuration. If a secret is present in the server-wide configuration, it can be disabled for this hook by setting the hook's secret to \"none\".\n\n```\nSecret = \"abcd\"\n```\n\n#### Timeout\n\nOverrides the server-wide Timeout setting. Any one command that runs longer than this value, in seconds, will be killed.\n\n```\nTimeout = 20\n```\n\n#### Commands\nA list of commands and arguments to be executed when this hook runs. For each command, the first list item is the executable and the remaining list items are the arguments to that executable. $PATH lookups are performed automatically if no directory is given.\n\n```\nCommands = [\n  [ \"echo\", \"Received commit, type = {{ .type }}, repo = {{.repository.name}}\"  ],\n  [ \"echo\", \"                 message = {{.commit.message}}\" ]\n]\n```\n\n### Command Templates\nEach string in the  `Dir`, `Env`, and `Command` fields is a template, which can substitute data from the event's payload. Any data in the event's JSON is accessible. The template syntax is provided by Go's `text/template` package, so [full documentation](https://godoc.org/text/template) can be found there.\n\nA template substitution is delimited by `{{ double brackets }}`. Generally, the first character inside brackets will be a `.`, which represents the entire JSON payload.\n\nThe template system also provides functions which can transform a particular item in some way. In addition to Go's built-in functions, unwebhook provides a `json` function to print an item and any subitems in JSON format.\n\nThe examples below do not represent all of the functions available, but are some of the more useful ones in this context.\n\n```\nThe repository data, in JSON format.\n{{ json .repository }}\n\nThe number of commits in the event.\n{{ len .commits }}\n\nThe first commit, translated into JSON. Note the pipe, which takes the resultof `index .commits 0` and sends it to the JSON function.\n{{ index .commits 0 | json }}\n\nThe same as above, expressed with nested function calls instead of pipes.\n{{ json (index .commits 0) }}\n\nThe timestamp of the first commit in the event.\n{{ (index .commits 0).timestamp }}\n\nThe name of the repository's owner.\n{{ .repository.owner.name }}\n```\n\n### Environment Variables\nIn addition to the templating system, the `Dir`, `Env`, and `Commands` members may have environment variables substituted using standard shell syntax such as `Dir=\"${HOME}/repos\"`. The environment variables are taken from the environment in which the server is running, not the environment that may be defined by an `Env` list.\n\n### Sample Configuration\n\nSome examples are below. Additional sample configuration files can be found in the `conf` directory of this repository.\n\n```\nListenAddress = \":8090\"\nCommandTimeout = 4\nLogDir = \"/var/log/unwebhook\"\nHookPaths = [ \"/etc/unwebhook.d\" ]\nAcceptIps = [ \"192.30.252.0/24\" ]\nSecret = \"abbadada\"\n\n[[Hook]]\nUrl = \"/sync-to-server\"\nDir = \"$HOME/repos/{{ .repository.name }}\"\nEnv = [ \"LOGNAME={{ .pusher.name }}\" ]\nAllowEvent = [\"push\"]\nTimeout = 25\n\nCommands = [\n  [\"git\", \"pull\"],\n  [\"rsync\", \"-r\", \".\", \"{{ .repository.name }}.company.com:/opt/var/files\"],\n]\n\n[[Hook]]\n# Here we have some script that can just process the JSON.\nUrl = \"/record-issue/:organization\"\nCommands = [ [ \"processevent\", \"{{.urlparams.organization}}\", \"{{ json . }}\" ] ]\n\n[[Hook]]\n# Run multiple commands through a single bash shell.\n# Add commit messages to a repository file in the ~/gitcommits directory.\n# Add the pusher's user ID to a repository file in the ~/gitusers directory\n# and delete duplicates.\n# Note that this isn't really thread-safe. Multiple requests hitting the hook at the\n# same time could cause the files to be out of sync since the operations performed by\n# the shell aren't all atomic.\nUrl = \"/bash-command\"\nPerCommit=true\nAcceptEvents = [\"push\"]\nCommands = [ \n   [ \"bash\", \"-c\", \"cd ~/gitcommits; echo {{.commit.id}} - {{ .commit.message }} \u003e\u003e {{.repository.name}}.txt;\" ],\n   [ \"bash\", \"-c\", \"cd ~/gitusers; echo {{.pusher.name}} \u003e\u003e {{.repository.name}}; sort {{.repository.name}} | uniq \u003e\u003e tmpfile; mv -f tmpfile {{.repository.name}}\" ] \n]\n\n[[Hook]]\n# Same as above, but just call a shell script that does it all.\nUrl = \"/call-my-script\"\nPerCommit=true\nAcceptEvents = [\"push\"]\nCommands = [ [ \"$HOME/bin/record-git.sh\", \"{{.commit.id}}\", \"{{.commit.message}}\", \"{{.repository.name}}\",\n   \"{{.pusher.name}}\" ]\n\n```\n\n## Acknowledgements\n\n* TOML parser from [BurntSushi/toml](https://github.com/BurntSushi/toml)\n* Logging code from [my fork](https://github.com/dimfeld/glog) of [Zenoss's glog fork](https://github.com/zenoss/glog)\n\nThis project also uses my [httpmuxtree](https://github.com/dimfeld/httptreemux) and [goconfig](https://github.com/dimfeld/goconfig) libraries.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimfeld%2Funwebhook","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimfeld%2Funwebhook","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimfeld%2Funwebhook/lists"}