Live video on the AT Protocol
1package main 2 3import ( 4 "encoding/json" 5 "flag" 6 "fmt" 7 "math/rand" 8 "net/http" 9 "os" 10 "sort" 11 "strings" 12 13 "github.com/go-git/go-git/v5" 14 "github.com/go-git/go-git/v5/plumbing" 15 "github.com/go-git/go-git/v5/plumbing/object" 16 "github.com/go-git/go-git/v5/plumbing/storer" 17 "github.com/google/uuid" 18) 19 20func main() { 21 err := makeGit() 22 if err != nil { 23 panic(err) 24 } 25} 26 27var tmpl = `package main 28 29var Version = "%s" 30var BuildTime = "%d" 31var UUID = "%s" 32` 33 34var tmplJS = ` 35export const version = "%s"; 36export const buildTime = "%d"; 37export const uuid = "%s"; 38` 39 40func gitlabURL() string { 41 CI_API_V4_URL := os.Getenv("CI_API_V4_URL") //nolint:all 42 CIProjectID := os.Getenv("CI_PROJECT_ID") 43 CI_API_V4_URL = strings.Replace(CI_API_V4_URL, "https://git.stream.place", "https://git-cloudflare.stream.place", 1) 44 return fmt.Sprintf("%s/projects/%s", CI_API_V4_URL, CIProjectID) 45} 46 47func gitlab(suffix string, dest any) { 48 u := fmt.Sprintf("%s%s", gitlabURL(), suffix) 49 50 req, err := http.Get(u) 51 if err != nil { 52 panic(err) 53 } 54 if err := json.NewDecoder(req.Body).Decode(dest); err != nil { 55 panic(err) 56 } 57} 58 59func gitlabList(suffix string) []map[string]any { 60 var result []map[string]any 61 gitlab(suffix, &result) 62 return result 63} 64 65func gitlabDict(suffix string) map[string]any { 66 var result map[string]any 67 gitlab(suffix, &result) 68 return result 69} 70 71func makeGit() error { 72 output := flag.String("o", "", "file to output to") 73 version := flag.Bool("v", false, "just print version") 74 env := flag.Bool("env", false, "print a bunch of useful environment variables") 75 doBranch := flag.Bool("branch", false, "print branch") 76 doRelease := flag.Bool("release", false, "print release json file") 77 javascript := flag.Bool("js", false, "print code in javascript format") 78 79 flag.Parse() 80 r, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) 81 if err != nil { 82 return err 83 } 84 85 // ... retrieving the HEAD reference 86 ref, err := r.Head() 87 if err != nil { 88 return err 89 } 90 c, err := r.CommitObject(ref.Hash()) 91 if err != nil { 92 return err 93 } 94 95 ts := c.Author.When.Unix() 96 rander := rand.New(rand.NewSource(ts)) 97 u, err := uuid.NewV7FromReader(rander) 98 if err != nil { 99 return err 100 } 101 g, err := PlainOpen(".") 102 if err != nil { 103 return err 104 } 105 desc, err := g.Describe(ref) 106 if err != nil { 107 return err 108 } 109 var out string 110 if *version { 111 out = desc 112 } else if *doBranch { 113 out = branch() 114 } else if *env { 115 StreamplaceBranch := branch() 116 outMap := map[string]string{} 117 outMap["STREAMPLACE_BRANCH"] = StreamplaceBranch 118 outMap["STREAMPLACE_VERSION"] = desc 119 outMap["STREAMPLACE_BRANCH"] = StreamplaceBranch 120 for _, arch := range []string{"amd64", "arm64"} { 121 k := fmt.Sprintf("STREAMPLACE_URL_LINUX_%s", strings.ToUpper(arch)) 122 v := fmt.Sprintf("%s/packages/generic/%s/%s/streamplace-%s-linux-%s.tar.gz", gitlabURL(), StreamplaceBranch, desc, desc, arch) 123 outMap[k] = v 124 macK := fmt.Sprintf("STREAMPLACE_URL_DARWIN_%s", strings.ToUpper(arch)) 125 macV := fmt.Sprintf("%s/packages/generic/%s/%s/streamplace-%s-darwin-%s.zip", gitlabURL(), StreamplaceBranch, desc, desc, arch) 126 outMap[macK] = macV 127 } 128 outMap["STREAMPLACE_DESKTOP_URL_WINDOWS_AMD64"] = fmt.Sprintf("%s/packages/generic/%s/%s/streamplace-desktop-%s-windows-amd64.exe", gitlabURL(), StreamplaceBranch, desc, desc) 129 for k, v := range outMap { 130 out = out + fmt.Sprintf("%s=%s\n", k, v) 131 } 132 } else if *doRelease { 133 outMap := map[string]any{} 134 outMap["name"] = desc 135 outMap["tag-name"] = desc 136 pkgs := gitlabList(fmt.Sprintf("/packages?order_by=created_at&sort=desc&package_name=%s", branch())) 137 id := pkgs[0]["id"].(float64) 138 pkgFiles := gitlabList(fmt.Sprintf("/packages/%d/package_files", int(id))) 139 outFiles := []string{} 140 sort.Slice(pkgFiles, func(i, j int) bool { 141 s1 := pkgFiles[i]["file_name"].(string) 142 s2 := pkgFiles[j]["file_name"].(string) 143 return s1 < s2 144 }) 145 for _, file := range pkgFiles { 146 fileJSON := map[string]string{ 147 "name": file["file_name"].(string), 148 "url": fmt.Sprintf("%s/packages/generic/%s/%s/%s", gitlabURL(), branch(), desc, file["file_name"].(string)), 149 } 150 bs, err := json.Marshal(fileJSON) 151 if err != nil { 152 return err 153 } 154 outFiles = append(outFiles, string(bs)) 155 } 156 outMap["assets-link"] = outFiles 157 changelog := gitlabDict(fmt.Sprintf("/repository/changelog?version=%s", desc)) 158 outMap["description"] = changelog["notes"] 159 bs, err := json.MarshalIndent(outMap, "", " ") 160 if err != nil { 161 return err 162 } 163 out = string(bs) 164 } else if *javascript { 165 out = fmt.Sprintf(tmplJS, desc, ts, u) 166 } else { 167 out = fmt.Sprintf(tmpl, desc, ts, u) 168 } 169 170 if *output != "" { 171 if err := os.WriteFile(*output, []byte(out), 0644); err != nil { 172 return err 173 } 174 } else { 175 fmt.Print(out) 176 } 177 return nil 178} 179 180func branch() string { 181 CICommitTag := os.Getenv("CI_COMMIT_TAG") 182 CICommitBranch := os.Getenv("CI_COMMIT_BRANCH") 183 if CICommitTag != "" { 184 return "latest" 185 } else if CICommitBranch != "" { 186 return strings.ReplaceAll(CICommitBranch, "/", "-") 187 } else { 188 panic("CI_COMMIT_TAG and CI_COMMIT_BRANCH undefined, can't get branch") 189 } 190} 191 192// Git struct wrapps Repository class from go-git to add a tag map used to perform queries when describing. 193type Git struct { 194 TagsMap map[plumbing.Hash]*plumbing.Reference 195 *git.Repository 196} 197 198// PlainOpen opens a git repository from the given path. It detects if the 199// repository is bare or a normal one. If the path doesn't contain a valid 200// repository ErrRepositoryNotExists is returned 201func PlainOpen(path string) (*Git, error) { 202 r, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{DetectDotGit: true}) 203 return &Git{ 204 make(map[plumbing.Hash]*plumbing.Reference), 205 r, 206 }, err 207} 208 209func (g *Git) getTagMap() error { 210 tags, err := g.Tags() 211 if err != nil { 212 return err 213 } 214 215 err = tags.ForEach(func(t *plumbing.Reference) error { 216 h, err := g.ResolveRevision(plumbing.Revision(t.Name())) 217 if err != nil { 218 return err 219 } 220 g.TagsMap[*h] = t 221 return nil 222 }) 223 224 return err 225} 226 227// Describe the reference as 'git describe --tags' will do 228func (g *Git) Describe(reference *plumbing.Reference) (string, error) { 229 230 // Fetch the reference log 231 cIter, err := g.Log(&git.LogOptions{ 232 // From: reference.Hash(), 233 Order: git.LogOrderCommitterTime, 234 }) 235 if err != nil { 236 return "", err 237 } 238 239 // Build the tag map 240 err = g.getTagMap() 241 if err != nil { 242 return "", err 243 } 244 245 // Search the tag 246 var tag *plumbing.Reference 247 var count int 248 err = cIter.ForEach(func(c *object.Commit) error { 249 t, ok := g.TagsMap[c.Hash] 250 if ok { 251 tag = t 252 return storer.ErrStop 253 } 254 count++ 255 return nil 256 }) 257 if err != nil { 258 return "", err 259 } 260 head, err := g.Head() 261 if err != nil { 262 return "", err 263 } 264 if count == 0 && os.Getenv("CI_COMMIT_TAG") != "" { 265 return fmt.Sprint(tag.Name().Short()), nil 266 } else { 267 return fmt.Sprintf("%s-%s", 268 tag.Name().Short(), 269 head.Hash().String()[0:8], 270 ), nil 271 } 272}