Live video on the AT Protocol
at natb/widgets-dashboard 373 lines 9.7 kB view raw
1package main 2 3import ( 4 "bytes" 5 "crypto/sha256" 6 "encoding/json" 7 "flag" 8 "fmt" 9 "io" 10 "math/rand" 11 "net/http" 12 "os" 13 "sort" 14 "strings" 15 "text/template" 16 17 "github.com/go-git/go-git/v5" 18 "github.com/go-git/go-git/v5/plumbing" 19 "github.com/go-git/go-git/v5/plumbing/object" 20 "github.com/go-git/go-git/v5/plumbing/storer" 21 "github.com/google/uuid" 22) 23 24func main() { 25 err := makeGit() 26 if err != nil { 27 panic(err) 28 } 29} 30 31var tmpl = `package main 32 33var Version = "%s" 34var BuildTime = "%d" 35var UUID = "%s" 36` 37 38var tmplJS = ` 39export const version = "%s"; 40export const buildTime = "%d"; 41export const uuid = "%s"; 42` 43 44func gitlabURL() string { 45 CI_API_V4_URL := os.Getenv("CI_API_V4_URL") //nolint:all 46 CIProjectID := os.Getenv("CI_PROJECT_ID") 47 CI_API_V4_URL = strings.Replace(CI_API_V4_URL, "https://git.stream.place", "https://git-cloudflare.stream.place", 1) 48 return fmt.Sprintf("%s/projects/%s", CI_API_V4_URL, CIProjectID) 49} 50 51func gitlab(suffix string, dest any) { 52 u := fmt.Sprintf("%s%s", gitlabURL(), suffix) 53 54 req, err := http.Get(u) 55 if err != nil { 56 panic(err) 57 } 58 if err := json.NewDecoder(req.Body).Decode(dest); err != nil { 59 panic(err) 60 } 61} 62 63func gitlabList(suffix string) []map[string]any { 64 var result []map[string]any 65 gitlab(suffix, &result) 66 return result 67} 68 69func gitlabDict(suffix string) map[string]any { 70 var result map[string]any 71 gitlab(suffix, &result) 72 return result 73} 74 75func makeGit() error { 76 output := flag.String("o", "", "file to output to") 77 version := flag.Bool("v", false, "just print version") 78 env := flag.Bool("env", false, "print a bunch of useful environment variables") 79 doBranch := flag.Bool("branch", false, "print branch") 80 doRelease := flag.Bool("release", false, "print release json file") 81 javascript := flag.Bool("js", false, "print code in javascript format") 82 homebrew := flag.Bool("homebrew", false, "print homebrew formula") 83 84 flag.Parse() 85 r, err := git.PlainOpenWithOptions(".", &git.PlainOpenOptions{DetectDotGit: true}) 86 if err != nil { 87 return err 88 } 89 90 // ... retrieving the HEAD reference 91 ref, err := r.Head() 92 if err != nil { 93 return err 94 } 95 c, err := r.CommitObject(ref.Hash()) 96 if err != nil { 97 return err 98 } 99 100 ts := c.Author.When.Unix() 101 rander := rand.New(rand.NewSource(ts)) 102 u, err := uuid.NewV7FromReader(rander) 103 if err != nil { 104 return err 105 } 106 g, err := PlainOpen(".") 107 if err != nil { 108 return err 109 } 110 desc, err := g.Describe(ref) 111 if err != nil { 112 return err 113 } 114 var out string 115 if *version { 116 out = desc 117 } else if *doBranch { 118 out = branch() 119 } else if *env { 120 StreamplaceBranch := branch() 121 outMap := map[string]string{} 122 outMap["STREAMPLACE_BRANCH"] = StreamplaceBranch 123 outMap["STREAMPLACE_VERSION"] = desc 124 outMap["STREAMPLACE_BRANCH"] = StreamplaceBranch 125 for _, arch := range []string{"amd64", "arm64"} { 126 k := fmt.Sprintf("STREAMPLACE_URL_LINUX_%s", strings.ToUpper(arch)) 127 v := fmt.Sprintf("%s/packages/generic/%s/%s/streamplace-%s-linux-%s.tar.gz", gitlabURL(), StreamplaceBranch, desc, desc, arch) 128 outMap[k] = v 129 macK := fmt.Sprintf("STREAMPLACE_URL_DARWIN_%s", strings.ToUpper(arch)) 130 macV := fmt.Sprintf("%s/packages/generic/%s/%s/streamplace-%s-darwin-%s.zip", gitlabURL(), StreamplaceBranch, desc, desc, arch) 131 outMap[macK] = macV 132 } 133 outMap["STREAMPLACE_DESKTOP_URL_WINDOWS_AMD64"] = fmt.Sprintf("%s/packages/generic/%s/%s/streamplace-desktop-%s-windows-amd64.exe", gitlabURL(), StreamplaceBranch, desc, desc) 134 for k, v := range outMap { 135 out = out + fmt.Sprintf("%s=%s\n", k, v) 136 } 137 } else if *doRelease { 138 outMap := map[string]any{} 139 outMap["name"] = desc 140 outMap["tag-name"] = desc 141 pkgs := gitlabList(fmt.Sprintf("/packages?order_by=created_at&sort=desc&package_name=%s", branch())) 142 id := pkgs[0]["id"].(float64) 143 pkgFiles := gitlabList(fmt.Sprintf("/packages/%d/package_files", int(id))) 144 outFiles := []string{} 145 sort.Slice(pkgFiles, func(i, j int) bool { 146 s1 := pkgFiles[i]["file_name"].(string) 147 s2 := pkgFiles[j]["file_name"].(string) 148 return s1 < s2 149 }) 150 for _, file := range pkgFiles { 151 fileJSON := map[string]string{ 152 "name": file["file_name"].(string), 153 "url": fmt.Sprintf("%s/packages/generic/%s/%s/%s", gitlabURL(), branch(), desc, file["file_name"].(string)), 154 } 155 bs, err := json.Marshal(fileJSON) 156 if err != nil { 157 return err 158 } 159 outFiles = append(outFiles, string(bs)) 160 } 161 outMap["assets-link"] = outFiles 162 changelog := gitlabDict(fmt.Sprintf("/repository/changelog?version=%s", desc)) 163 outMap["description"] = changelog["notes"] 164 bs, err := json.MarshalIndent(outMap, "", " ") 165 if err != nil { 166 return err 167 } 168 out = string(bs) 169 } else if *javascript { 170 out = fmt.Sprintf(tmplJS, desc, ts, u) 171 } else if *homebrew { 172 bs := bytes.Buffer{} 173 versionNoV := strings.TrimPrefix(desc, "v") 174 darwinAmd64File := fmt.Sprintf("streamplace-%s-darwin-amd64.tar.gz", desc) 175 darwinArm64File := fmt.Sprintf("streamplace-%s-darwin-arm64.tar.gz", desc) 176 linuxAmd64File := fmt.Sprintf("streamplace-%s-linux-amd64.tar.gz", desc) 177 linuxArm64File := fmt.Sprintf("streamplace-%s-linux-arm64.tar.gz", desc) 178 179 err = homebrewTmpl.Execute(&bs, Homebrew{ 180 Version: versionNoV, 181 DarwinArm64: getHash(darwinArm64File), 182 DarwinAmd64: getHash(darwinAmd64File), 183 LinuxArm64: getHash(linuxArm64File), 184 LinuxAmd64: getHash(linuxAmd64File), 185 }) 186 if err != nil { 187 return err 188 } 189 out = bs.String() 190 } else { 191 out = fmt.Sprintf(tmpl, desc, ts, u) 192 } 193 194 if *output != "" { 195 if err := os.WriteFile(*output, []byte(out), 0644); err != nil { 196 return err 197 } 198 } else { 199 fmt.Print(out) 200 } 201 return nil 202} 203 204func getHash(fileName string) string { 205 filePath := fmt.Sprintf("bin/%s", fileName) 206 f, err := os.Open(filePath) 207 if err != nil { 208 panic(err) 209 } 210 defer f.Close() 211 212 h := sha256.New() 213 buf := make([]byte, 1024*1024) // 1MB buffer 214 215 for { 216 n, err := f.Read(buf) 217 if n > 0 { 218 if _, err := h.Write(buf[:n]); err != nil { 219 panic(err) 220 } 221 } 222 if err != nil { 223 if err == io.EOF { 224 break 225 } 226 panic(err) 227 } 228 } 229 230 return fmt.Sprintf("%x", h.Sum(nil)) 231} 232 233func branch() string { 234 CICommitTag := os.Getenv("CI_COMMIT_TAG") 235 CICommitBranch := os.Getenv("CI_COMMIT_BRANCH") 236 if CICommitTag != "" { 237 return "latest" 238 } else if CICommitBranch != "" { 239 return strings.ReplaceAll(CICommitBranch, "/", "-") 240 } else { 241 panic("CI_COMMIT_TAG and CI_COMMIT_BRANCH undefined, can't get branch") 242 } 243} 244 245// Git struct wrapps Repository class from go-git to add a tag map used to perform queries when describing. 246type Git struct { 247 TagsMap map[plumbing.Hash]*plumbing.Reference 248 *git.Repository 249} 250 251// PlainOpen opens a git repository from the given path. It detects if the 252// repository is bare or a normal one. If the path doesn't contain a valid 253// repository ErrRepositoryNotExists is returned 254func PlainOpen(path string) (*Git, error) { 255 r, err := git.PlainOpenWithOptions(path, &git.PlainOpenOptions{DetectDotGit: true}) 256 return &Git{ 257 make(map[plumbing.Hash]*plumbing.Reference), 258 r, 259 }, err 260} 261 262func (g *Git) getTagMap() error { 263 tags, err := g.Tags() 264 if err != nil { 265 return err 266 } 267 268 err = tags.ForEach(func(t *plumbing.Reference) error { 269 h, err := g.ResolveRevision(plumbing.Revision(t.Name())) 270 if err != nil { 271 return err 272 } 273 g.TagsMap[*h] = t 274 return nil 275 }) 276 277 return err 278} 279 280// Describe the reference as 'git describe --tags' will do 281func (g *Git) Describe(reference *plumbing.Reference) (string, error) { 282 if os.Getenv("STREAMPLACE_VERSION_OVERRIDE") != "" { 283 return os.Getenv("STREAMPLACE_VERSION_OVERRIDE"), nil 284 } 285 286 // Fetch the reference log 287 cIter, err := g.Log(&git.LogOptions{ 288 // From: reference.Hash(), 289 Order: git.LogOrderCommitterTime, 290 }) 291 if err != nil { 292 return "", err 293 } 294 295 // Build the tag map 296 err = g.getTagMap() 297 if err != nil { 298 return "", err 299 } 300 301 // Search the tag 302 var tag *plumbing.Reference 303 var count int 304 err = cIter.ForEach(func(c *object.Commit) error { 305 t, ok := g.TagsMap[c.Hash] 306 if ok { 307 tag = t 308 return storer.ErrStop 309 } 310 count++ 311 return nil 312 }) 313 if err != nil { 314 return "", err 315 } 316 head, err := g.Head() 317 if err != nil { 318 return "", err 319 } 320 if count == 0 && os.Getenv("CI_COMMIT_TAG") != "" { 321 return fmt.Sprint(tag.Name().Short()), nil 322 } else { 323 return fmt.Sprintf("%s-%s", 324 tag.Name().Short(), 325 head.Hash().String()[0:8], 326 ), nil 327 } 328} 329 330type Homebrew struct { 331 Version string 332 DarwinArm64 string 333 DarwinAmd64 string 334 LinuxArm64 string 335 LinuxAmd64 string 336} 337 338var homebrewTmpl = template.Must(template.New("homebrew").Parse(` 339class Streamplace < Formula 340 desc "Live video for the AT Protocol. Solving video for everybody forever." 341 homepage "https://stream.place" 342 license "GPL-3.0-or-later" 343 version "{{.Version}}" 344 345 on_macos do 346 if Hardware::CPU.arm? 347 url "https://git-cloudflare.stream.place/api/v4/projects/1/packages/generic/latest/v{{.Version}}/streamplace-v{{.Version}}-darwin-arm64.tar.gz" 348 sha256 "{{.DarwinArm64}}" 349 end 350 351 if Hardware::CPU.intel? 352 url "https://git-cloudflare.stream.place/api/v4/projects/1/packages/generic/latest/v{{.Version}}/streamplace-v{{.Version}}-darwin-amd64.tar.gz" 353 sha256 "{{.DarwinAmd64}}" 354 end 355 end 356 357 on_linux do 358 if Hardware::CPU.arm? && Hardware::CPU.is_64_bit? 359 url "https://git-cloudflare.stream.place/api/v4/projects/1/packages/generic/latest/v{{.Version}}/streamplace-v{{.Version}}-linux-arm64.tar.gz" 360 sha256 "{{.LinuxArm64}}" 361 end 362 363 if Hardware::CPU.intel? 364 url "https://git-cloudflare.stream.place/api/v4/projects/1/packages/generic/latest/v{{.Version}}/streamplace-v{{.Version}}-linux-amd64.tar.gz" 365 sha256 "{{.LinuxAmd64}}" 366 end 367 end 368 369 def install 370 bin.install "streamplace" => "streamplace" 371 end 372end 373`))