github.1485827954.workers.dev/nektos/act@v0.2.63/cmd/notices.go (about) 1 package cmd 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "os" 9 "path/filepath" 10 "runtime" 11 "strings" 12 "time" 13 14 log "github.com/sirupsen/logrus" 15 ) 16 17 type Notice struct { 18 Level string `json:"level"` 19 Message string `json:"message"` 20 } 21 22 func displayNotices(input *Input) { 23 select { 24 case notices := <-noticesLoaded: 25 if len(notices) > 0 { 26 noticeLogger := log.New() 27 if input.jsonLogger { 28 noticeLogger.SetFormatter(&log.JSONFormatter{}) 29 } else { 30 noticeLogger.SetFormatter(&log.TextFormatter{ 31 DisableQuote: true, 32 DisableTimestamp: true, 33 PadLevelText: true, 34 }) 35 } 36 37 fmt.Printf("\n") 38 for _, notice := range notices { 39 level, err := log.ParseLevel(notice.Level) 40 if err != nil { 41 level = log.InfoLevel 42 } 43 noticeLogger.Log(level, notice.Message) 44 } 45 } 46 case <-time.After(time.Second * 1): 47 log.Debugf("Timeout waiting for notices") 48 } 49 } 50 51 var noticesLoaded = make(chan []Notice) 52 53 func loadVersionNotices(version string) { 54 go func() { 55 noticesLoaded <- getVersionNotices(version) 56 }() 57 } 58 59 const NoticeURL = "https://api.nektosact.com/notices" 60 61 func getVersionNotices(version string) []Notice { 62 if os.Getenv("ACT_DISABLE_VERSION_CHECK") == "1" { 63 return nil 64 } 65 66 noticeURL, err := url.Parse(NoticeURL) 67 if err != nil { 68 log.Error(err) 69 return nil 70 } 71 query := noticeURL.Query() 72 query.Add("os", runtime.GOOS) 73 query.Add("arch", runtime.GOARCH) 74 query.Add("version", version) 75 76 noticeURL.RawQuery = query.Encode() 77 78 client := &http.Client{} 79 req, err := http.NewRequest("GET", noticeURL.String(), nil) 80 if err != nil { 81 log.Debug(err) 82 return nil 83 } 84 85 etag := loadNoticesEtag() 86 if etag != "" { 87 log.Debugf("Conditional GET for notices etag=%s", etag) 88 req.Header.Set("If-None-Match", etag) 89 } 90 91 resp, err := client.Do(req) 92 if err != nil { 93 log.Debug(err) 94 return nil 95 } 96 97 newEtag := resp.Header.Get("Etag") 98 if newEtag != "" { 99 log.Debugf("Saving notices etag=%s", newEtag) 100 saveNoticesEtag(newEtag) 101 } 102 103 defer resp.Body.Close() 104 notices := []Notice{} 105 if resp.StatusCode == 304 { 106 log.Debug("No new notices") 107 return nil 108 } 109 if err := json.NewDecoder(resp.Body).Decode(¬ices); err != nil { 110 log.Debug(err) 111 return nil 112 } 113 114 return notices 115 } 116 117 func loadNoticesEtag() string { 118 p := etagPath() 119 content, err := os.ReadFile(p) 120 if err != nil { 121 log.Debugf("Unable to load etag from %s: %e", p, err) 122 } 123 return strings.TrimSuffix(string(content), "\n") 124 } 125 126 func saveNoticesEtag(etag string) { 127 p := etagPath() 128 err := os.WriteFile(p, []byte(strings.TrimSuffix(etag, "\n")), 0o600) 129 if err != nil { 130 log.Debugf("Unable to save etag to %s: %e", p, err) 131 } 132 } 133 134 func etagPath() string { 135 dir := filepath.Join(CacheHomeDir, "act") 136 if err := os.MkdirAll(dir, 0o777); err != nil { 137 log.Fatal(err) 138 } 139 return filepath.Join(dir, ".notices.etag") 140 }