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
     	if specified, requests for / will be redirected to this url
28
     	if specified, requests for / will be redirected to this url
29
   -repo string
29
   -repo string
30
     	the repository to redirect releases for, in user/repo format [required]
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
 ## Notes
35
 ## Notes
34
 
36
 
37
+### Root redirecting
38
+
35
 If the `-redirect` option is specified, then requests to the root (i.e. `/`)
39
 If the `-redirect` option is specified, then requests to the root (i.e. `/`)
36
 will be redirected to that URL, and no further processing will be done for
40
 will be redirected to that URL, and no further processing will be done for
37
 that request.
41
 that request.
38
 
42
 
43
+### Polling and webhooks
44
+
39
 Releases are refreshed at startup and then, by default, once an hour. You can
45
 Releases are refreshed at startup and then, by default, once an hour. You can
40
 customise this duration with the `-poll` option; setting the poll time to `0`
46
 customise this duration with the `-poll` option; setting the poll time to `0`
41
 will disable polling entirely other than at startup.
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
 Assets are matched on their name, so an asset attached to the latest release
68
 Assets are matched on their name, so an asset attached to the latest release
44
 named "myproject-installer-1.2.3.exe" will be available at the URL
69
 named "myproject-installer-1.2.3.exe" will be available at the URL
45
 `/myproject-installer-1.2.3.exe`.
70
 `/myproject-installer-1.2.3.exe`.
48
 if there is no latest release available all requests will respond with 500
73
 if there is no latest release available all requests will respond with 500
49
 internal server error.
74
 internal server error.
50
 
75
 
76
+### HTTPS
77
+
51
 The service listens only on HTTP, not HTTPS. For production use it should
78
 The service listens only on HTTP, not HTTPS. For production use it should
52
 be placed behind an SSL-terminating proxy such as Traefik, Nginx or HAProxy.
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
 ## Licence
93
 ## Licence
55
 
94
 
56
 This software is released under the MIT licence. See the LICENCE file for
95
 This software is released under the MIT licence. See the LICENCE file for

+ 27
- 6
main.go View File

18
 	owner string
18
 	owner string
19
 	repo string
19
 	repo string
20
 	redirect *string
20
 	redirect *string
21
+	webhookPath *string
21
 	ctx context.Context
22
 	ctx context.Context
22
 	client *github.Client
23
 	client *github.Client
23
 	release *github.RepositoryRelease
24
 	release *github.RepositoryRelease
39
 	w.WriteHeader(http.StatusTemporaryRedirect)
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
 	if "/" == request.RequestURI && len(*redirect) > 0 {
44
 	if "/" == request.RequestURI && len(*redirect) > 0 {
44
 		temporaryRedirect(w, *redirect)
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
 	if release == nil {
62
 	if release == nil {
49
 		w.WriteHeader(http.StatusInternalServerError)
63
 		w.WriteHeader(http.StatusInternalServerError)
50
 		_, _ = fmt.Fprint(w, "Unknown release")
64
 		_, _ = fmt.Fprint(w, "Unknown release")
51
-		return
65
+		return true
52
 	}
66
 	}
53
 
67
 
54
 	for _, asset := range release.Assets {
68
 	for _, asset := range release.Assets {
55
 		if "/" + *asset.Name == request.RequestURI {
69
 		if "/" + *asset.Name == request.RequestURI {
56
 			temporaryRedirect(w, *asset.BrowserDownloadURL)
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
 func parseRepo(fullRepo *string) error {
85
 func parseRepo(fullRepo *string) error {
92
 
112
 
93
 func main() {
113
 func main() {
94
 	redirect = flag.String("redirect", "", "if specified, requests for / will be redirected to this url")
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
 	var fullRepo = flag.String("repo", "", "the repository to redirect releases for, in user/repo format [required]")
116
 	var fullRepo = flag.String("repo", "", "the repository to redirect releases for, in user/repo format [required]")
96
 	var port = flag.Int("port", 8080, "the port to listen on for HTTP requests")
117
 	var port = flag.Int("port", 8080, "the port to listen on for HTTP requests")
97
 	var poll = flag.Int("poll", 3600, "the amount of time to wait between polling for releases; 0 to disable polling")
118
 	var poll = flag.Int("poll", 3600, "the amount of time to wait between polling for releases; 0 to disable polling")

Loading…
Cancel
Save