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(&notices); 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  }