github.com/grafana/pyroscope@v1.18.0/tools/update_examples.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"flag"
     7  	"fmt"
     8  	"log"
     9  	"net/http"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"regexp"
    14  	"slices"
    15  	"strconv"
    16  	"strings"
    17  )
    18  
    19  var ghToken string
    20  var all = flag.Bool("all", true, "")
    21  var golang = flag.Bool("go", false, "")
    22  var java = flag.Bool("java", false, "")
    23  var ruby = flag.Bool("ruby", false, "")
    24  var python = flag.Bool("python", false, "")
    25  var dotnet = flag.Bool("dotnet", false, "")
    26  var node = flag.Bool("node", false, "")
    27  var rust = flag.Bool("rust", false, "")
    28  
    29  // this program requires ruby, bundle, yarn, go to be installed
    30  func main() {
    31  
    32  	getGHToken()
    33  	flag.Parse()
    34  	if *all {
    35  		*golang = true
    36  		*java = true
    37  		*ruby = true
    38  		*python = true
    39  		*dotnet = true
    40  		*node = true
    41  		*rust = true
    42  	}
    43  
    44  	if *golang {
    45  		updateGolang()
    46  		updateGodeltaprof()
    47  		updateJfrParser()
    48  		s.sh("make go/mod")
    49  	}
    50  
    51  	if *java {
    52  		updateJava()
    53  		updateOtelProfilingJava()
    54  	}
    55  	if *ruby {
    56  		updateRuby()
    57  	}
    58  	if *python {
    59  		updatePython()
    60  	}
    61  	if *dotnet {
    62  		updateDotnet()
    63  	}
    64  	if *node {
    65  		updateNodeJS()
    66  	}
    67  	if *rust {
    68  		updateRust()
    69  	}
    70  }
    71  
    72  func getGHToken() {
    73  	ghToken = os.Getenv("GITHUB_TOKEN")
    74  }
    75  
    76  func extractNodeJSVersion(tag Tag) *version {
    77  	re := regexp.MustCompile(`(v)(\d+).(\d+).(\d+)`)
    78  	match := re.FindStringSubmatch(tag.Name)
    79  	if match != nil {
    80  		if match[1] == "v" {
    81  			major, err := strconv.Atoi(match[2])
    82  			requireNoError(err, "strconv")
    83  			minor, err := strconv.Atoi(match[3])
    84  			requireNoError(err, "strconv")
    85  			patch, err := strconv.Atoi(match[4])
    86  			requireNoError(err, "strconv")
    87  			return &version{major: major, minor: minor, patch: patch, tag: tag}
    88  		}
    89  	}
    90  	return nil
    91  }
    92  
    93  func updateNodeJS() {
    94  	tags := getTagsV("grafana/pyroscope-nodejs", extractNodeJSVersion)
    95  	last := tags[len(tags)-1]
    96  	fmt.Println(last)
    97  
    98  	replPackageJson := fmt.Sprintf(`    "@pyroscope/nodejs": "v%s",`, last.version())
    99  	rePackageJson := regexp.MustCompile(`    "@pyroscope/nodejs": "[^"]+",`)
   100  	for _, x := range []string{"express", "express-pull", "express-ts", "express-ts-inline", "tinyhttp"} {
   101  		path := filepath.Join("examples/language-sdk-instrumentation/nodejs", x)
   102  		replaceInplace(rePackageJson, filepath.Join(path, "package.json"), replPackageJson)
   103  		s.sh(fmt.Sprintf(`cd "%s"       && yarn`, path))
   104  	}
   105  }
   106  
   107  func updateRust() {
   108  	libTags := getTagsV("grafana/pyroscope-rs", extractRSVersion("lib"))
   109  	lastLibTag := libTags[len(libTags)-1]
   110  	fmt.Println(lastLibTag)
   111  	pprofRsTags := getTagsV("grafana/pyroscope-rs", extractRSVersion("pprofrs"))
   112  	lastPprofRsTag := pprofRsTags[len(pprofRsTags)-1]
   113  	fmt.Println(lastPprofRsTag)
   114  	s.sh(fmt.Sprintf("cd examples/language-sdk-instrumentation/rust/rideshare/server && cargo add pyroscope@^%s", lastLibTag.version()))
   115  	s.sh(fmt.Sprintf("cd examples/language-sdk-instrumentation/rust/rideshare/server && cargo add pyroscope_pprofrs@^%s", lastPprofRsTag.version()))
   116  	s.sh(fmt.Sprintf("cd examples/language-sdk-instrumentation/rust/basic && cargo add pyroscope@^%s", lastLibTag.version()))
   117  	s.sh(fmt.Sprintf("cd examples/language-sdk-instrumentation/rust/basic && cargo add pyroscope_pprofrs@^%s", lastPprofRsTag.version()))
   118  }
   119  
   120  func updateDotnet() {
   121  	tags := getTagsV("grafana/pyroscope-dotnet", extractDotnetVersion())
   122  	last := tags[len(tags)-1]
   123  	fmt.Println(last)
   124  
   125  	reDockerGlibc := regexp.MustCompile(`pyroscope/pyroscope-dotnet:\d+\.\d+\.\d+-glibc`)
   126  	replDockerGlibc := fmt.Sprintf("pyroscope/pyroscope-dotnet:%s-glibc", last.version())
   127  	replaceInplace(reDockerGlibc, "examples/language-sdk-instrumentation/dotnet/fast-slow/Dockerfile", replDockerGlibc)
   128  	replaceInplace(reDockerGlibc, "examples/language-sdk-instrumentation/dotnet/rideshare/Dockerfile", replDockerGlibc)
   129  	replaceInplace(reDockerGlibc, "examples/language-sdk-instrumentation/dotnet/web-new/Dockerfile", replDockerGlibc)
   130  	replaceInplace(reDockerGlibc, "docs/sources/configure-client/language-sdks/dotnet.md", replDockerGlibc)
   131  
   132  	reDockerMusl := regexp.MustCompile(`pyroscope/pyroscope-dotnet:\d+\.\d+\.\d+-musl`)
   133  	replDockerMusl := fmt.Sprintf("pyroscope/pyroscope-dotnet:%s-musl", last.version())
   134  	replaceInplace(reDockerMusl, "examples/language-sdk-instrumentation/dotnet/fast-slow/musl.Dockerfile", replDockerMusl)
   135  	replaceInplace(reDockerMusl, "examples/language-sdk-instrumentation/dotnet/rideshare/musl.Dockerfile", replDockerMusl)
   136  
   137  	reUrl := regexp.MustCompile(`https://github\.com/grafana/pyroscope-dotnet/releases/download/v\d+\.\d+\.\d+-pyroscope/pyroscope.\d+\.\d+\.\d+-glibc-x86_64.tar.gz`)
   138  	replUrl := fmt.Sprintf("https://github.com/grafana/pyroscope-dotnet/releases/download/v%s-pyroscope/pyroscope.%s-glibc-x86_64.tar.gz", last.version(), last.version())
   139  	replaceInplace(reUrl, "docs/sources/configure-client/language-sdks/dotnet.md", replUrl)
   140  }
   141  
   142  func updatePython() {
   143  	tags := getTagsV("grafana/pyroscope-rs", extractRSVersion("python"))
   144  	last := tags[len(tags)-1]
   145  	fmt.Println(last)
   146  
   147  	re := regexp.MustCompile(`pyroscope-io==\d+\.\d+\.\d+`)
   148  	repl := fmt.Sprintf("pyroscope-io==%s", last.version())
   149  	replaceInplace(re, "examples/language-sdk-instrumentation/python/simple/requirements.txt", repl)
   150  	replaceInplace(re, "examples/language-sdk-instrumentation/python/rideshare/flask/Dockerfile", repl)
   151  	replaceInplace(re, "examples/language-sdk-instrumentation/python/rideshare/fastapi/Dockerfile", repl)
   152  	replaceInplace(re, "examples/language-sdk-instrumentation/python/rideshare/django/app/requirements.txt", repl)
   153  
   154  }
   155  
   156  func updateRuby() {
   157  	tags := getTagsV("grafana/pyroscope-rs", extractRSVersion("ruby"))
   158  	last := tags[len(tags)-1]
   159  	fmt.Println(last)
   160  
   161  	re := regexp.MustCompile(`gem ['"]pyroscope['"].*`)
   162  	repl := fmt.Sprintf("gem 'pyroscope', '= %s'", last.version())
   163  	replaceInplace(re, "examples/language-sdk-instrumentation/ruby/rideshare/Gemfile", repl)
   164  	replaceInplace(re, "examples/language-sdk-instrumentation/ruby/rideshare_rails/Gemfile", repl)
   165  	replaceInplace(re, "examples/language-sdk-instrumentation/ruby/simple/Gemfile", repl)
   166  
   167  	s.sh("cd examples/language-sdk-instrumentation/ruby/rideshare       && bundle update pyroscope")
   168  	s.sh("cd examples/language-sdk-instrumentation/ruby/rideshare_rails && bundle update pyroscope")
   169  	s.sh("cd examples/language-sdk-instrumentation/ruby/simple          && bundle update pyroscope")
   170  }
   171  
   172  func updateJava() {
   173  	tags := getTagsV("grafana/pyroscope-java", extractGoVersion(""))
   174  	last := tags[len(tags)-1]
   175  	fmt.Println(last)
   176  	reJarURL := regexp.MustCompile(`https://github\.com/grafana/pyroscope-java/releases/download/(v\d+\.\d+\.\d+)/pyroscope\.jar`)
   177  	lastJarURL := "https://github.com/grafana/pyroscope-java/releases/download/" + last.versionV() + "/pyroscope.jar"
   178  	replaceInplace(reJarURL, "examples/language-sdk-instrumentation/java/fib/Dockerfile", lastJarURL)
   179  	replaceInplace(reJarURL, "examples/language-sdk-instrumentation/java/simple/Dockerfile", lastJarURL)
   180  	replaceInplace(reJarURL, "examples/tracing/java/Dockerfile", lastJarURL)
   181  	replaceInplace(reJarURL, "examples/language-sdk-instrumentation/java/rideshare/Dockerfile", lastJarURL)
   182  
   183  	reGradelDep := regexp.MustCompile(`implementation\("io\.pyroscope:agent:\d+\.\d+\.\d+"\)`)
   184  	lastGradleDep := fmt.Sprintf("implementation(\"io.pyroscope:agent:%s\")", last.version())
   185  	replaceInplace(reGradelDep, "examples/language-sdk-instrumentation/java/rideshare/build.gradle.kts", lastGradleDep)
   186  	replaceInplace(reGradelDep, "examples/tracing/java/build.gradle.kts", lastGradleDep)
   187  	replaceInplace(reGradelDep, "docs/sources/configure-client/language-sdks/java.md", lastGradleDep)
   188  
   189  	reMaven := regexp.MustCompile(`<version>\d+\.\d+\.\d+</version>`)
   190  	replMaven := fmt.Sprintf("<version>%s</version>", last.version())
   191  	replaceInplace(reMaven, "docs/sources/configure-client/language-sdks/java.md", replMaven)
   192  
   193  }
   194  
   195  func updateOtelProfilingJava() {
   196  	tags := getTagsV("grafana/otel-profiling-java", extractGoVersion(""))
   197  	last := tags[len(tags)-1]
   198  	reJarURL := regexp.MustCompile(`https://github\.com/grafana/otel-profiling-java/releases/download/(v\d+\.\d+\.\d+)/pyroscope-otel\.jar`)
   199  	lastJarURL := "https://github.com/grafana/otel-profiling-java/releases/download/" + last.versionV() + "/pyroscope-otel.jar"
   200  	replaceInplace(reJarURL, "docs/sources/configure-client/trace-span-profiles/java-span-profiles.md", lastJarURL)
   201  	replaceInplace(reJarURL, "examples/tracing/java/Dockerfile", lastJarURL)
   202  }
   203  
   204  func replaceInplace(re *regexp.Regexp, file string, replacement string) {
   205  	bs, err := os.ReadFile(file)
   206  	requireNoError(err, "read file "+file)
   207  	str1 := string(bs)
   208  	str2 := re.ReplaceAllString(str1, replacement)
   209  	err = os.WriteFile(file, []byte(str2), 066)
   210  	requireNoError(err, "write file "+file)
   211  }
   212  
   213  func updateGodeltaprof() {
   214  	tags := getTagsV("grafana/pyroscope-go", extractGoVersion("godeltaprof"))
   215  	last := tags[len(tags)-1]
   216  	log.Println(last.tag.Name)
   217  	s.sh(" go get github.com/grafana/pyroscope-go/godeltaprof@" + last.versionV() + " && go mod tidy")
   218  	s.sh("cd ./examples/language-sdk-instrumentation/golang-push/rideshare &&  go get github.com/grafana/pyroscope-go/godeltaprof@" + last.versionV() +
   219  		" && go mod tidy")
   220  	s.sh("cd ./examples/language-sdk-instrumentation/golang-push/simple    &&  go get github.com/grafana/pyroscope-go/godeltaprof@" + last.versionV() +
   221  		" && go mod tidy")
   222  }
   223  
   224  var s = sh{}
   225  
   226  func updateGolang() {
   227  	tags := getTagsV("grafana/pyroscope-go", extractGoVersion(""))
   228  	last2 := tags[len(tags)-1]
   229  	last := last2
   230  	log.Println(last.tag.Name)
   231  	s.sh("cd ./examples/language-sdk-instrumentation/golang-push/rideshare &&  go get github.com/grafana/pyroscope-go@" + last.versionV() +
   232  		" && go mod tidy")
   233  	s.sh("cd ./examples/language-sdk-instrumentation/golang-push/simple    &&  go get github.com/grafana/pyroscope-go@" + last.versionV() +
   234  		" && go mod tidy")
   235  }
   236  
   237  func extractGoVersion(module string) func(tag Tag) *version {
   238  	return func(tag Tag) *version {
   239  		re := regexp.MustCompile(`([^/]*)/?v(\d+).(\d+).(\d+)`)
   240  		match := re.FindStringSubmatch(tag.Name)
   241  		if match != nil {
   242  			//fmt.Println(len(match), match)
   243  			if match[1] == module {
   244  				major, err := strconv.Atoi(match[2])
   245  				requireNoError(err, "strconv")
   246  				minor, err := strconv.Atoi(match[3])
   247  				requireNoError(err, "strconv")
   248  				patch, err := strconv.Atoi(match[4])
   249  				requireNoError(err, "strconv")
   250  				return &version{major: major, minor: minor, patch: patch, tag: tag}
   251  			}
   252  		}
   253  		return nil
   254  	}
   255  }
   256  
   257  func extractRSVersion(module string) func(tag Tag) *version {
   258  	return func(tag Tag) *version {
   259  		re := regexp.MustCompile(`(\S+)-(\d+).(\d+).(\d+)`)
   260  		match := re.FindStringSubmatch(tag.Name)
   261  		if match != nil {
   262  			//fmt.Println(len(match), match)
   263  			if match[1] == module {
   264  				major, err := strconv.Atoi(match[2])
   265  				requireNoError(err, "strconv")
   266  				minor, err := strconv.Atoi(match[3])
   267  				requireNoError(err, "strconv")
   268  				patch, err := strconv.Atoi(match[4])
   269  				requireNoError(err, "strconv")
   270  				return &version{major: major, minor: minor, patch: patch, tag: tag}
   271  			}
   272  		}
   273  		return nil
   274  	}
   275  }
   276  
   277  func updateJfrParser() {
   278  	parserVersions := getTagsV("grafana/jfr-parser", extractGoVersion(""))
   279  	parserVersion := parserVersions[len(parserVersions)-1]
   280  	fmt.Printf("jfr-parser  %+v\n", parserVersion)
   281  
   282  	s.sh(" go get github.com/grafana/jfr-parser@" + parserVersion.versionV())
   283  }
   284  
   285  func extractDotnetVersion() func(tag Tag) *version {
   286  	return func(tag Tag) *version {
   287  		re := regexp.MustCompile(`v(\d+).(\d+).(\d+)(-pyroscope)?$`)
   288  		match := re.FindStringSubmatch(tag.Name)
   289  		if match != nil {
   290  			fmt.Println(len(match), match)
   291  
   292  			major, err := strconv.Atoi(match[1])
   293  			requireNoError(err, "strconv")
   294  			minor, err := strconv.Atoi(match[2])
   295  			requireNoError(err, "strconv")
   296  			patch, err := strconv.Atoi(match[3])
   297  			requireNoError(err, "strconv")
   298  			return &version{major: major, minor: minor, patch: patch, tag: tag}
   299  
   300  		}
   301  		return nil
   302  	}
   303  }
   304  
   305  func getTagsV(repo string, extractVersion func(Tag) *version) []version {
   306  	tags := getTags(repo)
   307  	fmt.Println(tags)
   308  	versions := []version{}
   309  
   310  	for _, tag := range tags {
   311  		v := extractVersion(tag)
   312  		if v != nil {
   313  			versions = append(versions, *v)
   314  		}
   315  	}
   316  	slices.SortFunc(versions, compareVersion)
   317  	return versions
   318  }
   319  
   320  type version struct {
   321  	major int
   322  	minor int
   323  	patch int
   324  
   325  	tag Tag
   326  }
   327  
   328  func (v *version) versionV() string {
   329  	return fmt.Sprintf("v%d.%d.%d", v.major, v.minor, v.patch)
   330  }
   331  
   332  func (v *version) version() string {
   333  	return fmt.Sprintf("%d.%d.%d", v.major, v.minor, v.patch)
   334  }
   335  
   336  func compareVersion(a, b version) int {
   337  	cmp := func(a, b int) int {
   338  		if a < b {
   339  			return -1
   340  		}
   341  		if a > b {
   342  			return 1
   343  		}
   344  		return 0
   345  	}
   346  
   347  	if c := cmp(a.major, b.major); c != 0 {
   348  		return c
   349  	}
   350  	if c := cmp(a.minor, b.minor); c != 0 {
   351  		return c
   352  	}
   353  	if c := cmp(a.patch, b.patch); c != 0 {
   354  		return c
   355  	}
   356  
   357  	return 0
   358  }
   359  
   360  func getTags(repo string) []Tag {
   361  	// get "https://api.github.com/repos/<owner>/<repo>/tags"
   362  	var res []Tag
   363  	page := 1
   364  	for {
   365  		var pageTags []Tag
   366  		url := "https://api.github.com/repos/" + repo + "/tags?page=" + strconv.Itoa(page) + "&per_page=100"
   367  		log.Printf("GET %s", url)
   368  		req, err := http.NewRequest("GET", url, nil)
   369  		req.Header.Set("Authorization", "Bearer "+strings.TrimSpace(ghToken))
   370  		requireNoError(err, "new request")
   371  		resp, err := http.DefaultClient.Do(req)
   372  		requireNoError(err, "do request")
   373  		if resp.StatusCode != 200 {
   374  			log.Fatalf("status code %d", resp.StatusCode)
   375  		}
   376  		defer resp.Body.Close()
   377  		err = json.NewDecoder(resp.Body).Decode(&pageTags)
   378  		requireNoError(err, "decoding json")
   379  		res = append(res, pageTags...)
   380  		if len(pageTags) == 0 {
   381  			break
   382  		}
   383  		page++
   384  	}
   385  	return res
   386  }
   387  
   388  type Tag struct {
   389  	Name       string `json:"name"`
   390  	ZipballUrl string `json:"zipball_url"`
   391  	TarballUrl string `json:"tarball_url"`
   392  	Commit     struct {
   393  		Sha string `json:"sha"`
   394  		Url string `json:"url"`
   395  	} `json:"commit"`
   396  	NodeId string `json:"node_id"`
   397  }
   398  
   399  type sh struct {
   400  	wd string
   401  }
   402  
   403  func (s *sh) sh(sh string) (string, string) {
   404  	return s.cmd("/bin/sh", "-c", sh)
   405  }
   406  
   407  func (s *sh) cmd(cmdArgs ...string) (string, string) {
   408  	log.Printf("cmd %s\n", strings.Join(cmdArgs, " "))
   409  	cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
   410  	cmd.Dir = s.wd
   411  	stdout := bytes.NewBuffer(nil)
   412  	stderr := bytes.NewBuffer(nil)
   413  	cmd.Stdout = stdout
   414  	cmd.Stderr = stderr
   415  	err := cmd.Run()
   416  	fmt.Println(stdout.String())
   417  	fmt.Println(stderr.String())
   418  	requireNoError(err, strings.Join(cmdArgs, " "))
   419  	return stdout.String(), stderr.String()
   420  }
   421  
   422  func requireNoError(err error, msg string) {
   423  	if err != nil {
   424  		log.Fatalf("msg %s err %v", msg, err)
   425  	}
   426  }