github.com/grafana/pyroscope@v1.18.0/tools/update-contributors/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"log"
     8  	"net/http"
     9  	"os"
    10  	"strings"
    11  )
    12  
    13  type contributor struct {
    14  	Login     string `json:"login"`
    15  	AvatarURL string `json:"avatar_url"`
    16  	HTMLURL   string `json:"html_url"`
    17  }
    18  
    19  func fetchContributors(ctx context.Context, owner, repo string) ([]contributor, error) {
    20  	url := fmt.Sprintf("https://api.github.com/repos/%s/%s/contributors?per_page=200", owner, repo)
    21  	req, err := http.NewRequest("GET", url, nil)
    22  	if err != nil {
    23  		return nil, err
    24  	}
    25  	req = req.WithContext(ctx)
    26  
    27  	resp, err := http.DefaultClient.Do(req)
    28  	if err != nil {
    29  		return nil, err
    30  	}
    31  	defer resp.Body.Close()
    32  
    33  	if resp.StatusCode != http.StatusOK {
    34  		return nil, fmt.Errorf("unexpected status code %d", resp.StatusCode)
    35  	}
    36  
    37  	var contributors []contributor
    38  	if err := json.NewDecoder(resp.Body).Decode(&contributors); err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	return contributors, nil
    43  }
    44  
    45  func generateContributors() (string, error) {
    46  	ctx := context.Background()
    47  	contributors, err := fetchContributors(ctx, "grafana", "pyroscope")
    48  	if err != nil {
    49  		return "", err
    50  	}
    51  
    52  	sb := strings.Builder{}
    53  
    54  	limit := 9 * 7
    55  
    56  	for _, c := range contributors {
    57  		// filter bots
    58  		if strings.HasSuffix(c.Login, "[bot]") || c.Login == "pyroscopebot" {
    59  			continue
    60  		}
    61  		sb.WriteString(`<a href="`)
    62  		sb.WriteString(c.HTMLURL)
    63  		sb.WriteString(`"><img src="`)
    64  		sb.WriteString(c.AvatarURL)
    65  		sb.WriteString(`" title="`)
    66  		sb.WriteString(c.Login)
    67  		sb.WriteString(`" width="80" height="80"></a>`)
    68  		sb.WriteByte('\n')
    69  		limit--
    70  		if limit == 0 {
    71  			break
    72  		}
    73  	}
    74  
    75  	return sb.String(), nil
    76  }
    77  
    78  const (
    79  	marker     = "[//]: contributor-faces"
    80  	readmeFile = "README.md"
    81  )
    82  
    83  func replaceReadme() error {
    84  	b, err := os.ReadFile(readmeFile)
    85  	if err != nil {
    86  		return err
    87  	}
    88  	s := string(b)
    89  
    90  	start := strings.Index(s, marker)
    91  	if start == -1 {
    92  		return fmt.Errorf("could not find marker %q", marker)
    93  	}
    94  	start += len(marker) + 1
    95  
    96  	end := strings.Index(s[start:], marker)
    97  	if end == -1 {
    98  		return fmt.Errorf("could not find end marker %q", marker)
    99  	}
   100  	end += start
   101  
   102  	contributor, err := generateContributors()
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	f, err := os.Create(readmeFile)
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	defer f.Close()
   113  
   114  	_, err = f.WriteString(s[:start])
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	_, err = f.WriteString(contributor)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	_, err = f.WriteString("\n")
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	_, err = f.WriteString(s[end:])
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	return nil
   135  }
   136  
   137  func main() {
   138  	if err := replaceReadme(); err != nil {
   139  		log.Fatal(err)
   140  	}
   141  }