Browse Source

Support for receiving webhooks

master
Chris Smith 5 years ago
parent
commit
379ff587d2
2 changed files with 66 additions and 6 deletions
  1. 39
    0
      README.md
  2. 27
    6
      main.go

+ 39
- 0
README.md View File

@@ -28,18 +28,43 @@ Usage of github-release-redirector:
28 28
     	if specified, requests for / will be redirected to this url
29 29
   -repo string
30 30
     	the repository to redirect releases for, in user/repo format [required]
31
+  -webhook string
32
+    	full path to receive release webhooks from GitHub on
31 33
 ```
32 34
 
33 35
 ## Notes
34 36
 
37
+### Root redirecting
38
+
35 39
 If the `-redirect` option is specified, then requests to the root (i.e. `/`)
36 40
 will be redirected to that URL, and no further processing will be done for
37 41
 that request.
38 42
 
43
+### Polling and webhooks
44
+
39 45
 Releases are refreshed at startup and then, by default, once an hour. You can
40 46
 customise this duration with the `-poll` option; setting the poll time to `0`
41 47
 will disable polling entirely other than at startup.
42 48
 
49
+As an alternative to polling, you can configure a webhook URL with the
50
+`-webhook` argument. This must be the full URL that GitHub will call
51
+when a release is made, and it is advisable to include a secret in the
52
+URL to avoid any client on the Internet being able to trigger a refresh.
53
+
54
+For example if you run with `-webhook /webhook/gjrCBVy7`, you
55
+should configure GitHub to send pull requests to
56
+`https://yourserver.example.com/webook/gjrCBVy7`. You should configure
57
+the WebHook to only send individual events, and select only the
58
+`Releases` option.
59
+
60
+The contents of the webhook are not used; it is merely used as a signal
61
+to start a refresh using the GitHub API.
62
+
63
+When running with the `-webhook` option it is recommended to also disable
64
+polling with `-poll 0`, or set the polling time to a much larger value.
65
+
66
+### Asset matching
67
+
43 68
 Assets are matched on their name, so an asset attached to the latest release
44 69
 named "myproject-installer-1.2.3.exe" will be available at the URL
45 70
 `/myproject-installer-1.2.3.exe`.
@@ -48,9 +73,23 @@ Any url not mapped to an asset is responded to with a 404 not found error;
48 73
 if there is no latest release available all requests will respond with 500
49 74
 internal server error.
50 75
 
76
+### HTTPS
77
+
51 78
 The service listens only on HTTP, not HTTPS. For production use it should
52 79
 be placed behind an SSL-terminating proxy such as Traefik, Nginx or HAProxy.
53 80
 
81
+## Contributing/further work
82
+
83
+This performs the job I wrote it to do, so it's unlikely I'll be developing
84
+any further features. If you're using this and would like to send a pull
85
+request for a feature I'd be happy to review and merge.
86
+
87
+In particular I'd welcome the following:
88
+
89
+- [ ] HTTPS support (specifying certs/keys/etc)
90
+- [ ] Support for GitHub's secret digests, instead of using a secret URL
91
+- [ ] Parsing of the WebHook content to avoid refreshing needlessly
92
+
54 93
 ## Licence
55 94
 
56 95
 This software is released under the MIT licence. See the LICENCE file for

+ 27
- 6
main.go View File

@@ -18,6 +18,7 @@ var (
18 18
 	owner string
19 19
 	repo string
20 20
 	redirect *string
21
+	webhookPath *string
21 22
 	ctx context.Context
22 23
 	client *github.Client
23 24
 	release *github.RepositoryRelease
@@ -39,27 +40,46 @@ func temporaryRedirect(w http.ResponseWriter, url string) {
39 40
 	w.WriteHeader(http.StatusTemporaryRedirect)
40 41
 }
41 42
 
42
-func serve(w http.ResponseWriter, request *http.Request) {
43
+func serveStaticPaths(w http.ResponseWriter, request *http.Request) bool {
43 44
 	if "/" == request.RequestURI && len(*redirect) > 0 {
44 45
 		temporaryRedirect(w, *redirect)
45
-		return
46
+		return true
46 47
 	}
47 48
 
49
+	if *webhookPath == request.RequestURI {
50
+		log.Println("Received webhook, starting a refresh")
51
+		go func() {
52
+			fetchLatest()
53
+		}()
54
+		w.WriteHeader(http.StatusOK)
55
+		return true
56
+	}
57
+
58
+	return false
59
+}
60
+
61
+func serveAssets(w http.ResponseWriter, request *http.Request) bool {
48 62
 	if release == nil {
49 63
 		w.WriteHeader(http.StatusInternalServerError)
50 64
 		_, _ = fmt.Fprint(w, "Unknown release")
51
-		return
65
+		return true
52 66
 	}
53 67
 
54 68
 	for _, asset := range release.Assets {
55 69
 		if "/" + *asset.Name == request.RequestURI {
56 70
 			temporaryRedirect(w, *asset.BrowserDownloadURL)
57
-			return
71
+			return true
58 72
 		}
59 73
 	}
60 74
 
61
-	w.WriteHeader(http.StatusNotFound)
62
-	_, _ = fmt.Fprint(w, "Asset not found in release ", *release.Name)
75
+	return false
76
+}
77
+
78
+func serve(w http.ResponseWriter, request *http.Request) {
79
+	if !serveStaticPaths(w, request) && !serveAssets(w, request) {
80
+		w.WriteHeader(http.StatusNotFound)
81
+		_, _ = fmt.Fprint(w, "Asset not found in release ", *release.Name)
82
+	}
63 83
 }
64 84
 
65 85
 func parseRepo(fullRepo *string) error {
@@ -92,6 +112,7 @@ func initTicker(seconds int) {
92 112
 
93 113
 func main() {
94 114
 	redirect = flag.String("redirect", "", "if specified, requests for / will be redirected to this url")
115
+	webhookPath = flag.String("webhook", "", "full path to receive release webhooks from GitHub on")
95 116
 	var fullRepo = flag.String("repo", "", "the repository to redirect releases for, in user/repo format [required]")
96 117
 	var port = flag.Int("port", 8080, "the port to listen on for HTTP requests")
97 118
 	var poll = flag.Int("poll", 3600, "the amount of time to wait between polling for releases; 0 to disable polling")

Loading…
Cancel
Save