{"id":13764081,"url":"https://github.com/rjeczalik/gh","last_synced_at":"2025-04-11T10:32:34.785Z","repository":{"id":28351800,"uuid":"31865514","full_name":"rjeczalik/gh","owner":"rjeczalik","description":"Scriptable server and net/http middleware for GitHub Webhooks.","archived":false,"fork":false,"pushed_at":"2018-10-28T15:27:35.000Z","size":127,"stargazers_count":82,"open_issues_count":2,"forks_count":12,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-08-03T15:06:09.921Z","etag":null,"topics":["github","github-webhooks","golang","webhook-command"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rjeczalik.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":"2015-03-08T21:04:05.000Z","updated_at":"2024-08-03T15:06:09.922Z","dependencies_parsed_at":"2022-09-05T18:30:26.997Z","dependency_job_id":null,"html_url":"https://github.com/rjeczalik/gh","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjeczalik%2Fgh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjeczalik%2Fgh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjeczalik%2Fgh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rjeczalik%2Fgh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rjeczalik","download_url":"https://codeload.github.com/rjeczalik/gh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223466499,"owners_count":17149770,"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":["github","github-webhooks","golang","webhook-command"],"created_at":"2024-08-03T15:01:12.507Z","updated_at":"2024-11-07T05:45:56.174Z","avatar_url":"https://github.com/rjeczalik.png","language":"Go","funding_links":[],"categories":["Version Control","Utility","版本控制","校验库","校验库`用于校验的库`"],"sub_categories":["Utility/Miscellaneous","实用程序/Miscellaneous","HTTP Clients","Advanced Console UIs","高級控制台界面","版本控制","版本控制`版本控制相关库`","交流","Fail injection","\u003cspan id=\"高级控制台用户界面-advanced-console-uis\"\u003e高级控制台用户界面 Advanced Console UIs\u003c/span\u003e","高级控制台界面"],"readme":"gh [![GoDoc](https://godoc.org/github.com/rjeczalik/gh?status.svg)](https://godoc.org/github.com/rjeczalik/gh) [![Build Status](https://img.shields.io/travis/rjeczalik/gh/master.svg)](https://travis-ci.org/rjeczalik/gh \"linux_amd64\") [![Build status](https://img.shields.io/appveyor/ci/rjeczalik/gh.svg)](https://ci.appveyor.com/project/rjeczalik/gh \"windows_amd64\") [![Coverage Status](https://img.shields.io/coveralls/rjeczalik/gh/master.svg)](https://coveralls.io/r/rjeczalik/gh?branch=master)\n======\n\nCommands and packages for GitHub services.\n\n*Installation*\n\n```\n~ $ go get -u github.com/rjeczalik/gh\n```\n\n### webhook [![GoDoc](https://godoc.org/github.com/rjeczalik/gh/webhook?status.svg)](https://godoc.org/github.com/rjeczalik/gh/webhook)\n\nPackage webhook implements middleware for GitHub Webhooks. User provides webhook service object that handles events delivered by GitHub. Webhook handler verifies payload signature delivered along with the event, unmarshals it to corresponding event struct and dispatches control to user service.\n\n*Documentation*\n\n[https://godoc.org/github.com/rjeczalik/gh/webhook](https://godoc.org/github.com/rjeczalik/gh/webhook)\n\n*Examples*\n\nNotify Slack's channel about recent push:\n```go\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/rjeczalik/gh/webhook\"\n)\n\nvar (\n\tsecret  = flag.String(\"secret\", \"\", \"GitHub webhook secret\")\n\ttoken   = flag.String(\"token\", \"\", \"Slack API token\")\n\tchannel = flag.String(\"channel\", \"\", \"Slack channel name\")\n)\n\ntype slack struct{}\n\nfunc (s slack) Push(e *webhook.PushEvent) {\n\tconst format = \"https://slack.com/api/chat.postMessage?token=%s\u0026channel=%s\u0026text=%s\"\n\ttext := url.QueryEscape(fmt.Sprintf(\"%s pushed to %s\", e.Pusher.Email, e.Repository.Name))\n\tif _, err := http.Get(fmt.Sprintf(format, *token, *channel, text)); err != nil {\n\t\tlog.Println(err)\n\t}\n}\n\nfunc main() {\n\tflag.Parse()\n\tlog.Fatal(http.ListenAndServe(\":8080\", webhook.New(*secret, slack{})))\n}\n```\nNotify HipChat's room about recent push:\n```go\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"strings\"\n\n\t\"github.com/rjeczalik/gh/webhook\"\n)\n\nvar (\n\tsecret = flag.String(\"secret\", \"\", \"GitHub webhook secret\")\n\ttoken  = flag.String(\"token\", \"\", \"HipChat personal API token\")\n\troom   = flag.String(\"room\", \"\", \"HipChat room ID\")\n)\n\ntype hipchat struct{}\n\nfunc (h hipchat) Push(e *webhook.PushEvent) {\n\turl := fmt.Sprintf(\"https://api.hipchat.com/v2/room/%s/notification\", *room)\n\tbody := fmt.Sprintf(`{\"message\":\"%s pushed to %s\"}`, e.Pusher.Email, e.Repository.Name)\n\treq, err := http.NewRequest(\"POST\", url, strings.NewReader(body))\n\tif err != nil {\n\t\tlog.Println(err)\n\t\treturn\n\t}\n\treq.Header.Set(\"Content-Type\", \"application/json\")\n\treq.Header.Set(\"Authorization\", \"Bearer \"+*token)\n\tif _, err := http.DefaultClient.Do(req); err != nil {\n\t\tlog.Println(err)\n\t}\n}\n\nfunc main() {\n\tflag.Parse()\n\tlog.Fatal(http.ListenAndServe(\":8080\", webhook.New(*secret, hipchat{})))\n}\n```\n\n### cmd/webhook [![GoDoc](https://godoc.org/github.com/rjeczalik/gh/cmd/webhook?status.svg)](https://godoc.org/github.com/rjeczalik/gh/cmd/webhook)\n\nCommand webhook starts a web server which listens on GitHub's POST requests. The payload of each request is verified against its signature, unmarshalled into corresponding event struct and the applied to the template script provided by a user.\n\n*Examples*\n\nNotify Slack's channel about recent push:\n```bash\n~ $ cat \u003eslack.tsc \u003c\u003cEOF\n\u003e {{with $e := .}}\n\u003e   {{if eq $e.Name \"push\"}}\n\u003e     {{with $text := (urlquery (printf \"%s pushed to %s\" $e.Payload.Pusher.Email $e.Payload.Repository.Name))}}\n\u003e     {{with $url := (printf \"https://slack.com/api/chat.postMessage?token=%s\u0026channel=%s\u0026text=%s\" $e.Args.Token $e.Args.Channel $text)}}\n\u003e       {{exec \"curl\" \"-X\" \"GET\" $url}}\n\u003e     {{end}}\n\u003e     {{end}}\n\u003e   {{end}}\n\u003e {{end}}\n\u003e EOF\n```\n```\n~ $ webhook -secret secret123 slack.tsc -- -token token123 -channel CH123\n```\nNotify HipChat's room about recent push:\n```bash\n~ $ cat \u003ehipchat.tsc \u003c\u003cEOF\n\u003e {{with $e := .}}\n\u003e   {{if eq $e.Name \"push\"}}\n\u003e     {{with $auth := (printf \"authorization: bearer %s\" $e.Args.Token)}}\n\u003e     {{with $msg := (printf \"{\\\"message_format\\\": \\\"text\\\", \\\"message\\\": \\\"%s pushed to %s\\\"}\" $e.Payload.Pusher.Email $e.Payload.Repository.Name)}}\n\u003e     {{with $url := (printf \"https://api.hipchat.com/v2/room/%s/notification\" $e.Args.Room)}}\n\u003e       {{exec \"curl\" \"-h\" \"content-type: application/json\" \"-h\" $auth \"-x\" \"post\" \"-d\" $msg $url | log}}\n\u003e     {{end}}\n\u003e     {{end}}\n\u003e     {{end}}\n\u003e   {{end}}\n\u003e {{end}}\n\u003e EOF\n```\n```\n~ $ webhook -secret secret123 hipchat.tsc -- -token token123 -room 123\n```\n\n### Contributing to the `webhook` package\n\nOccasionally it may happen that serving webhook may fail with serialization error. Since structs for event payloads are go-generated from either on-line GitHub Developer documentation or real GitHub's requests, they may not contain all fields or have wrong field types. If you notice webhook failures like the following:\n\n![delivery details](https://i.imgur.com/s6JgGdb.png)\n\nTake the following steps to fix the problem:\n\n- restart your webhook command with `-dump` flag (or wrap your `*webhook.Handler` with [webhook.Dump](https://godoc.org/github.com/rjeczalik/gh/webhook#Dump)):\n\n```\n~ $ webhook -dump /tmp -secret secret123 handler.tsc\n```\n\n- redeliver the event, it's going to fail again, but this time it will be dumped to  `/tmp/pull_request-ef748000-d078-11e4-91b6-77fc544482ea.json` file (named after the event and its `X-GitHub-Delivery` header)\n- copy the file to the testdata directory of webhook package and regenerate payloads:\n\n```\n~ $ scp core@remote:/tmp/pull_request-ef748000-d078-11e4-91b6-77fc544482ea.json ~/src/github.com/rjeczalik/gh/webhook/testdata\n~ $ go generate github.com/rjeczalik/gh/...\n~ $ go test github.com/rjeczalik/gh/...\n```\n\n- if both the `go generate` and `go test` succeed, send pull request with modified `payload.go` and the JSON file\n- if either of them fail, re-run them command with `-v` flag and create issue with original error message and the verbose outputs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frjeczalik%2Fgh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frjeczalik%2Fgh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frjeczalik%2Fgh/lists"}