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 }