github.com/telepresenceio/telepresence/v2@v2.20.0-pro.6.0.20240517030216-236ea954e789/build-aux/genversion/main.go (about) 1 // Copyright 2021 Datawire. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package main 16 17 import ( 18 "crypto/md5" 19 "encoding/base64" 20 "fmt" 21 "io/fs" 22 "os" 23 //nolint:depguard // This short script has no logging and no Contexts. 24 "os/exec" 25 "path/filepath" 26 "strings" 27 28 "github.com/blang/semver/v4" 29 ignore "github.com/sabhiram/go-gitignore" 30 ) 31 32 // isReleased returns true if a release tag exist for the given version 33 // A release tag is a tag that represents a semver version, without pre-version 34 // or build suffixes, that is prefixed with "v", e.g. "v1.2.3" is considered 35 // a release tag whereas "v1.2.3-rc.3" isn't. 36 func isReleased(v semver.Version) bool { 37 v.Build = nil 38 v.Pre = nil 39 return exec.Command("git", "describe", "v"+v.String()).Run() == nil 40 } 41 42 // dirMD5 computes the MD5 checksum of all files found when recursively 43 // traversing a directory, skipping .gitignore's, _test/, and _test.go. The 44 // general idea is to avoid rebuilds and pushes when repeatedly running tests, 45 // even if the tests themselves actually change. 46 func dirMD5(root string) ([]byte, error) { 47 ign, err := ignore.CompileIgnoreFile(filepath.Join(root, ".gitignore")) 48 if err != nil { 49 return nil, err 50 } 51 d := md5.New() 52 testMach := fmt.Sprintf("_test%c", filepath.Separator) 53 isTest := func(path string) bool { 54 return strings.Contains(path, testMach) || strings.HasSuffix(path, "_test.go") 55 } 56 err = filepath.Walk(root, func(path string, info fs.FileInfo, err error) error { 57 if err == nil && info.Mode().IsRegular() && !(ign.MatchesPath(path) || isTest(path)) { 58 var data []byte 59 if data, err = os.ReadFile(path); err == nil { 60 d.Write(data) 61 } 62 } 63 return err 64 }) 65 if err != nil { 66 return nil, err 67 } 68 return d.Sum(make([]byte, 0, md5.Size)), nil 69 } 70 71 func Main() error { 72 cmd := exec.Command("git", "describe", "--tags", "--match=v*") 73 cmd.Stderr = os.Stderr 74 gitDescBytes, err := cmd.Output() 75 if err != nil { 76 return fmt.Errorf("unable to git describe: %w", err) 77 } 78 gitDescStr := strings.TrimSuffix(strings.TrimPrefix(string(gitDescBytes), "v"), "\n") 79 gitDescVer, err := semver.Parse(gitDescStr) 80 if err != nil { 81 return fmt.Errorf("unable to parse semver %s: %w", gitDescStr, err) 82 } 83 84 // Bump to next patch version only if the version has been released. 85 if isReleased(gitDescVer) { 86 gitDescVer.Patch++ 87 } 88 89 // If an additional arg has been used, we include it in the tag 90 if len(os.Args) >= 2 { 91 // gitDescVer.Pre[0] contains the number of commits since the last tag and the 92 // shortHash with a 'g' appended. Since the first section isn't relevant, 93 // we get the shortHash this way since we don't need that extra information. 94 cmd = exec.Command("git", "rev-parse", "--short", "HEAD") 95 cmd.Stderr = os.Stderr 96 shortHash, err := cmd.Output() 97 if err != nil { 98 return fmt.Errorf("unable to git rev-parse: %w", err) 99 } 100 if _, err := fmt.Printf("v%d.%d.%d-%s-%s\n", gitDescVer.Major, gitDescVer.Minor, gitDescVer.Patch, os.Args[1], shortHash); err != nil { 101 return fmt.Errorf("unable to printf: %w", err) 102 } 103 return nil 104 } 105 106 // Append a mangled md5 if the directory is dirty. 107 cmd = exec.Command("git", "status", "--short") 108 cmd.Stderr = os.Stderr 109 statusBytes, err := cmd.Output() 110 if err != nil { 111 return fmt.Errorf("unable to git rev-parse: %w", err) 112 } 113 if len(statusBytes) > 0 { 114 var md5Out []byte 115 md5Out, err = dirMD5(".") 116 if err != nil { 117 return fmt.Errorf("unable to compute MD5: %w", err) 118 } 119 120 b64 := base64.RawURLEncoding.EncodeToString(md5Out) 121 b64 = strings.ReplaceAll(b64, "_", "Z") 122 b64 = strings.ReplaceAll(b64, "-", "z") 123 _, err = fmt.Printf("v%s-%s\n", gitDescVer, b64) 124 } else { 125 _, err = fmt.Printf("v%s\n", gitDescVer) 126 } 127 return err 128 } 129 130 func main() { 131 if err := Main(); err != nil { 132 fmt.Fprintf(os.Stderr, "%s: error: %v\n", filepath.Base(os.Args[0]), err) 133 os.Exit(1) 134 } 135 }