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