| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 1 | package dockerimg |
| 2 | |
| 3 | import ( |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 4 | "crypto/sha256" |
| Philip Zeyliger | 9df94b5 | 2025-05-18 03:43:14 +0000 | [diff] [blame] | 5 | _ "embed" // Using underscore import to keep embed package for go:embed directive |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 6 | "encoding/hex" |
| 7 | "encoding/json" |
| 8 | "fmt" |
| David Crawshaw | 2a5bd6d | 2025-04-30 14:29:46 -0700 | [diff] [blame] | 9 | "io" |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 10 | "net/http" |
| Earl Lee | 2e463fb | 2025-04-17 11:22:22 -0700 | [diff] [blame] | 11 | ) |
| 12 | |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 13 | // DefaultImage is intended to ONLY be used by the pushdockerimg.go script. |
| David Crawshaw | 2a5bd6d | 2025-04-30 14:29:46 -0700 | [diff] [blame] | 14 | func DefaultImage() (name, dockerfile, tag string) { |
| 15 | return dockerImgName, dockerfileBase, dockerfileBaseHash() |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 16 | } |
| 17 | |
| Philip Zeyliger | bce3a13 | 2025-04-30 22:03:39 +0000 | [diff] [blame] | 18 | const ( |
| 19 | dockerImgRepo = "boldsoftware/sketch" |
| 20 | dockerImgName = "ghcr.io/" + dockerImgRepo |
| 21 | ) |
| David Crawshaw | 5bff650 | 2025-04-26 09:11:40 -0700 | [diff] [blame] | 22 | |
| David Crawshaw | 2a5bd6d | 2025-04-30 14:29:46 -0700 | [diff] [blame] | 23 | func dockerfileBaseHash() string { |
| 24 | h := sha256.New() |
| 25 | io.WriteString(h, dockerfileBase) |
| 26 | return hex.EncodeToString(h.Sum(nil))[:32] |
| 27 | } |
| David Crawshaw | 1112949 | 2025-04-25 20:41:53 -0700 | [diff] [blame] | 28 | |
| Philip Zeyliger | 9df94b5 | 2025-05-18 03:43:14 +0000 | [diff] [blame] | 29 | //go:embed Dockerfile.base |
| 30 | var dockerfileBaseData []byte |
| David Crawshaw | be10fa9 | 2025-04-18 01:16:00 -0700 | [diff] [blame] | 31 | |
| Philip Zeyliger | 9df94b5 | 2025-05-18 03:43:14 +0000 | [diff] [blame] | 32 | // dockerfileBase is the content of the base Dockerfile |
| 33 | var dockerfileBase = string(dockerfileBaseData) |
| David Crawshaw | be10fa9 | 2025-04-18 01:16:00 -0700 | [diff] [blame] | 34 | |
| David Crawshaw | 2a5bd6d | 2025-04-30 14:29:46 -0700 | [diff] [blame] | 35 | func 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 | |
| 79 | func 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 | } |