package feed import ( "fmt" "net/http" "strings" ) type dockerHubRepositoryTagsResponse struct { Results []dockerHubRepositoryTagResponse `json:"results"` } type dockerHubRepositoryTagResponse struct { Name string `json:"name"` LastPushed string `json:"tag_last_pushed"` } const dockerHubOfficialRepoTagURLFormat = "https://hub.docker.com/_/%s/tags?name=%s" const dockerHubRepoTagURLFormat = "https://hub.docker.com/r/%s/tags?name=%s" const dockerHubTagsURLFormat = "https://hub.docker.com/v2/namespaces/%s/repositories/%s/tags" const dockerHubSpecificTagURLFormat = "https://hub.docker.com/v2/namespaces/%s/repositories/%s/tags/%s" func fetchLatestDockerHubRelease(request *ReleaseRequest) (*AppRelease, error) { nameParts := strings.Split(request.Repository, "/") if len(nameParts) > 2 { return nil, fmt.Errorf("invalid repository name: %s", request.Repository) } else if len(nameParts) == 1 { nameParts = []string{"library", nameParts[0]} } tagParts := strings.SplitN(nameParts[1], ":", 2) var requestURL string if len(tagParts) == 2 { requestURL = fmt.Sprintf(dockerHubSpecificTagURLFormat, nameParts[0], tagParts[0], tagParts[1]) } else { requestURL = fmt.Sprintf(dockerHubTagsURLFormat, nameParts[0], nameParts[1]) } httpRequest, err := http.NewRequest("GET", requestURL, nil) if err != nil { return nil, err } if request.Token != nil { httpRequest.Header.Add("Authorization", "Bearer "+(*request.Token)) } var tag *dockerHubRepositoryTagResponse if len(tagParts) == 1 { response, err := decodeJsonFromRequest[dockerHubRepositoryTagsResponse](defaultClient, httpRequest) if err != nil { return nil, err } if len(response.Results) == 0 { return nil, fmt.Errorf("no tags found for repository: %s", request.Repository) } tag = &response.Results[0] } else { response, err := decodeJsonFromRequest[dockerHubRepositoryTagResponse](defaultClient, httpRequest) if err != nil { return nil, err } tag = &response } var repo string var displayName string var notesURL string if len(tagParts) == 1 { repo = nameParts[1] } else { repo = tagParts[0] } if nameParts[0] == "library" { displayName = repo notesURL = fmt.Sprintf(dockerHubOfficialRepoTagURLFormat, repo, tag.Name) } else { displayName = nameParts[0] + "/" + repo notesURL = fmt.Sprintf(dockerHubRepoTagURLFormat, displayName, tag.Name) } return &AppRelease{ Source: ReleaseSourceDockerHub, NotesUrl: notesURL, Name: displayName, Version: tag.Name, TimeReleased: parseRFC3339Time(tag.LastPushed), }, nil }