blob: d20da12d4b11da90a06935460ab1dbe4bf040d86 [file] [log] [blame]
Earl Lee2e463fb2025-04-17 11:22:22 -07001package dockerimg
2
3import (
Earl Lee2e463fb2025-04-17 11:22:22 -07004 "crypto/sha256"
Philip Zeyliger9df94b52025-05-18 03:43:14 +00005 _ "embed" // Using underscore import to keep embed package for go:embed directive
Earl Lee2e463fb2025-04-17 11:22:22 -07006 "encoding/hex"
7 "encoding/json"
8 "fmt"
David Crawshaw2a5bd6d2025-04-30 14:29:46 -07009 "io"
Earl Lee2e463fb2025-04-17 11:22:22 -070010 "net/http"
Earl Lee2e463fb2025-04-17 11:22:22 -070011)
12
David Crawshaw11129492025-04-25 20:41:53 -070013// DefaultImage is intended to ONLY be used by the pushdockerimg.go script.
David Crawshaw2a5bd6d2025-04-30 14:29:46 -070014func DefaultImage() (name, dockerfile, tag string) {
15 return dockerImgName, dockerfileBase, dockerfileBaseHash()
David Crawshaw11129492025-04-25 20:41:53 -070016}
17
Philip Zeyligerbce3a132025-04-30 22:03:39 +000018const (
19 dockerImgRepo = "boldsoftware/sketch"
20 dockerImgName = "ghcr.io/" + dockerImgRepo
21)
David Crawshaw5bff6502025-04-26 09:11:40 -070022
David Crawshaw2a5bd6d2025-04-30 14:29:46 -070023func dockerfileBaseHash() string {
24 h := sha256.New()
25 io.WriteString(h, dockerfileBase)
26 return hex.EncodeToString(h.Sum(nil))[:32]
27}
David Crawshaw11129492025-04-25 20:41:53 -070028
Philip Zeyliger9df94b52025-05-18 03:43:14 +000029//go:embed Dockerfile.base
30var dockerfileBaseData []byte
David Crawshawbe10fa92025-04-18 01:16:00 -070031
Philip Zeyliger9df94b52025-05-18 03:43:14 +000032// dockerfileBase is the content of the base Dockerfile
33var dockerfileBase = string(dockerfileBaseData)
David Crawshawbe10fa92025-04-18 01:16:00 -070034
David Crawshaw2a5bd6d2025-04-30 14:29:46 -070035func readPublishedTags() ([]string, error) {
36 req, err := http.NewRequest("GET", "https://ghcr.io/token?service=ghcr.io&scope=repository:"+dockerImgRepo+":pull", nil)
37 if err != nil {
38 return nil, fmt.Errorf("token: %w", err)
39 }
40 res, err := http.DefaultClient.Do(req)
41 if err != nil {
42 return nil, fmt.Errorf("token: %w", err)
43 }
44 body, err := io.ReadAll(res.Body)
45 res.Body.Close()
46 if err != nil || res.StatusCode != 200 {
47 return nil, fmt.Errorf("token: %d: %s: %w", res.StatusCode, body, err)
48 }
49 var tokenBody struct {
50 Token string `json:"token"`
51 }
52 if err := json.Unmarshal(body, &tokenBody); err != nil {
53 return nil, fmt.Errorf("token: %w: %s", err, body)
54 }
55
56 req, err = http.NewRequest("GET", "https://ghcr.io/v2/"+dockerImgRepo+"/tags/list", nil)
57 if err != nil {
58 return nil, fmt.Errorf("tags: %w", err)
59 }
60 req.Header.Set("Authorization", "Bearer "+tokenBody.Token)
61 res, err = http.DefaultClient.Do(req)
62 if err != nil {
63 return nil, fmt.Errorf("tags: %w", err)
64 }
65 body, err = io.ReadAll(res.Body)
66 res.Body.Close()
67 if err != nil || res.StatusCode != 200 {
68 return nil, fmt.Errorf("tags: %d: %s: %w", res.StatusCode, body, err)
69 }
70 var tags struct {
71 Tags []string `json:"tags"`
72 }
73 if err := json.Unmarshal(body, &tags); err != nil {
74 return nil, fmt.Errorf("tags: %w: %s", err, body)
75 }
76 return tags.Tags, nil
77}
78
79func checkTagExists(tag string) error {
80 tags, err := readPublishedTags()
81 if err != nil {
82 return fmt.Errorf("check tag exists: %w", err)
83 }
84 for _, t := range tags {
85 if t == tag {
86 return nil // found it
87 }
88 }
89 return fmt.Errorf("check tag exists: %q not found in %v", tag, tags)
90}