Small HTTP server that redirects to artifacts from the latest github release for a project
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

main.go 3.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. package main
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "github.com/google/go-github/github"
  7. "log"
  8. "net/http"
  9. "os"
  10. "os/signal"
  11. "strings"
  12. "syscall"
  13. "time"
  14. )
  15. var (
  16. owner string
  17. repo string
  18. redirect *string
  19. ctx context.Context
  20. client *github.Client
  21. release *github.RepositoryRelease
  22. ticker *time.Ticker
  23. )
  24. func fetchLatest() {
  25. latest, _, err := client.Repositories.GetLatestRelease(ctx, owner, repo)
  26. if err != nil {
  27. log.Println("Error retrieving latest release", err)
  28. return
  29. }
  30. log.Printf("Found latest release: %s\n", *latest.Name)
  31. release = latest
  32. }
  33. func temporaryRedirect(w http.ResponseWriter, url string) {
  34. w.Header().Add("Location", url)
  35. w.WriteHeader(http.StatusTemporaryRedirect)
  36. }
  37. func serve(w http.ResponseWriter, request *http.Request) {
  38. if "/" == request.RequestURI && len(*redirect) > 0 {
  39. temporaryRedirect(w, *redirect)
  40. return
  41. }
  42. if release == nil {
  43. w.WriteHeader(http.StatusInternalServerError)
  44. _, _ = fmt.Fprint(w, "Unknown release")
  45. return
  46. }
  47. for _, asset := range release.Assets {
  48. if "/" + *asset.Name == request.RequestURI {
  49. temporaryRedirect(w, *asset.BrowserDownloadURL)
  50. return
  51. }
  52. }
  53. w.WriteHeader(http.StatusNotFound)
  54. _, _ = fmt.Fprint(w, "Asset not found in release ", *release.Name)
  55. }
  56. func parseRepo(fullRepo *string) error {
  57. if len(*fullRepo) == 0 {
  58. return fmt.Errorf("the repository option must be specified")
  59. }
  60. if strings.Count(*fullRepo, "/") != 1 {
  61. return fmt.Errorf("the repository must be specified in `user/repo` format")
  62. }
  63. repoParts := strings.Split(*fullRepo, "/")
  64. owner = repoParts[0]
  65. repo = repoParts[1]
  66. return nil
  67. }
  68. func initTicker(seconds int) {
  69. if seconds > 0 {
  70. log.Printf("Starting ticker for polling once every %d seconds.\n", seconds)
  71. ticker = time.NewTicker(time.Duration(seconds) * time.Second)
  72. go func() {
  73. for range ticker.C {
  74. fetchLatest()
  75. }
  76. }()
  77. } else {
  78. log.Println("Not starting ticker; performing one off fetch and relying on webhooks.")
  79. }
  80. fetchLatest()
  81. }
  82. func main() {
  83. redirect = flag.String("redirect", "", "if specified, requests for / will be redirected to this url")
  84. var fullRepo = flag.String("repo", "", "the repository to redirect releases for, in user/repo format [required]")
  85. var port = flag.Int("port", 8080, "the port to listen on for HTTP requests")
  86. var poll = flag.Int("poll", 3600, "the amount of time to wait between polling for releases; 0 to disable polling")
  87. flag.Parse()
  88. if err := parseRepo(fullRepo); err != nil {
  89. _, _ = fmt.Fprintf(os.Stderr, "Error: %s\n\n", err.Error())
  90. flag.Usage()
  91. return
  92. }
  93. client = github.NewClient(nil)
  94. var cancel context.CancelFunc
  95. ctx, cancel = context.WithCancel(context.Background())
  96. c := make(chan os.Signal, 1)
  97. signal.Notify(c, os.Interrupt)
  98. signal.Notify(c, syscall.SIGTERM)
  99. go func() {
  100. for sig := range c {
  101. cancel()
  102. if ticker != nil {
  103. ticker.Stop()
  104. }
  105. log.Printf("Received %s, exiting.\n", sig.String())
  106. os.Exit(0)
  107. }
  108. }()
  109. initTicker(*poll)
  110. log.Printf("Listing on :%d\n", *port)
  111. server := &http.Server{
  112. Addr: fmt.Sprintf(":%d", *port),
  113. Handler: http.HandlerFunc(serve),
  114. }
  115. err := server.ListenAndServe()
  116. if err != nil {
  117. log.Println("Error listening for requests on port ", port, err)
  118. }
  119. }