Skip to content

Commit

Permalink
[Docker Hub] fix docker hub images search (#15488)
Browse files Browse the repository at this point in the history
* fix docker hub images search

* add changelog entry, apply lint fixes

* Update CHANGELOG.md and optimise images

---------

Co-authored-by: raycastbot <[email protected]>
  • Loading branch information
imposibrus and raycastbot authored Dec 11, 2024
1 parent 42c64e5 commit 7b1da94
Show file tree
Hide file tree
Showing 8 changed files with 104 additions and 67 deletions.
4 changes: 4 additions & 0 deletions extensions/dockerhub/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Docker Hub Changelog

## [Fix images search] - 2024-12-11

- Fixed docker hub images search

## [Fix] - 2023-07-28

- Fixed Pull Image Shortcut ⌘+⇧+p instead of ⌘+p
Expand Down
23 changes: 23 additions & 0 deletions extensions/dockerhub/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion extensions/dockerhub/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"license": "MIT",
"contributors": [
"tonka3000",
"BalliAsghar"
"BalliAsghar",
"imposibrus"
],
"commands": [
{
Expand Down Expand Up @@ -92,6 +93,7 @@
"@typescript-eslint/parser": "^5.29.0",
"eslint": "^8.18.0",
"eslint-config-prettier": "^8.5.0",
"prettier": "^3.3.3",
"react-devtools": "^4.24.7",
"typescript": "^4.7.4"
},
Expand Down
14 changes: 7 additions & 7 deletions extensions/dockerhub/src/components/Search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import { Action, ActionPanel, Color, Icon, List, showToast, Toast } from "@rayca
import { useCallback, useState, useEffect } from "react";
import { Hub } from "../lib/hub/hub";
import SearchTags from "./SearchTags";
import { SearchTypeEnum, Summary, ItemAccessory } from "../lib/hub/types";
import { SearchTypeEnum, ImageSearchResult, ItemAccessory } from "../lib/hub/types";
import { mapFromToIcon } from "../lib/hub/utils";
import { pullImage, checkImageExists } from "../lib/hub/docker";

export default function Search(props: { searchType: SearchTypeEnum }) {
const [images, setImages] = useState<Summary[]>([]);
const [images, setImages] = useState<ImageSearchResult[]>([]);
const [loading, setLoading] = useState<boolean>(true);
const [existingImages, setExistingImages] = useState<Set<string>>(new Set());

Expand All @@ -17,8 +17,8 @@ export default function Search(props: { searchType: SearchTypeEnum }) {
setLoading(true);
try {
const hub = new Hub();
const result = await hub.search({ q: text, page_size: 100, type: props.searchType }, abortCtrl.signal);
setImages(result.summaries ?? []);
const response = await hub.search({ query: text, size: 100, type: props.searchType }, abortCtrl.signal);
setImages(response.results ?? []);
} catch (err) {
showToast({
style: Toast.Style.Failure,
Expand Down Expand Up @@ -56,7 +56,7 @@ export default function Search(props: { searchType: SearchTypeEnum }) {
{images.map((item) => (
<List.Item
key={item.slug}
icon={mapFromToIcon(item.from)}
icon={mapFromToIcon(item.source)}
title={item.name}
subtitle={item.short_description}
actions={
Expand Down Expand Up @@ -92,9 +92,9 @@ export default function Search(props: { searchType: SearchTypeEnum }) {
tooltip: `${item.star_count} Stars`,
},
{
text: item.pull_count,
text: item.rate_plans[0].repositories[0].pull_count,
icon: Icon.Download,
tooltip: `${item.pull_count} Downloads`,
tooltip: `${item.rate_plans[0].repositories[0].pull_count} Downloads`,
},
].filter((item) => item !== null) as ItemAccessory[]
}
Expand Down
55 changes: 27 additions & 28 deletions extensions/dockerhub/src/components/SearchTags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,34 +42,33 @@ export default function SearchTags(props: { repo: string; hub?: Hub }) {

return (
<List isLoading={loading} onSearchTextChange={search} throttle>
{tags.map(
(tag) =>
tag.images?.map((imageTag) => (
<List.Item
key={`${tag.id}-${imageTag.digest}`}
title={tag.name}
subtitle={tag.last_updated ? `${tag.last_updated} by ${tag.last_updater_username}` : ""}
actions={
<ActionPanel>
<Action.CopyToClipboard title="Copy Pull Command" content={`docker pull ${props.repo}:${tag.name}`} />
<Action.CopyToClipboard title="Copy Name with Tag" content={`${props.repo}:${tag.name}`} />
<Action.OpenInBrowser url={imageTag.url ?? ""} />
<Action.CopyToClipboard title="Copy URL" content={imageTag.url ?? ""} />
<Action
title="Pull Image"
onAction={() => pullImage(`${props.repo}:${tag.name}`)}
icon={Icon.Download}
shortcut={{ modifiers: ["cmd"], key: "p" }}
/>
</ActionPanel>
}
accessories={[
{
text: `${imageTag.os_arch ?? ""} ${imageTag.sizeHuman ? imageTag.sizeHuman : ""}`,
},
]}
/>
)),
{tags.map((tag) =>
tag.images?.map((imageTag) => (
<List.Item
key={`${tag.id}-${imageTag.digest}`}
title={tag.name}
subtitle={tag.last_updated ? `${tag.last_updated} by ${tag.last_updater_username}` : ""}
actions={
<ActionPanel>
<Action.CopyToClipboard title="Copy Pull Command" content={`docker pull ${props.repo}:${tag.name}`} />
<Action.CopyToClipboard title="Copy Name with Tag" content={`${props.repo}:${tag.name}`} />
<Action.OpenInBrowser url={imageTag.url ?? ""} />
<Action.CopyToClipboard title="Copy URL" content={imageTag.url ?? ""} />
<Action
title="Pull Image"
onAction={() => pullImage(`${props.repo}:${tag.name}`)}
icon={Icon.Download}
shortcut={{ modifiers: ["cmd"], key: "p" }}
/>
</ActionPanel>
}
accessories={[
{
text: `${imageTag.os_arch ?? ""} ${imageTag.sizeHuman ? imageTag.sizeHuman : ""}`,
},
]}
/>
)),
)}
</List>
);
Expand Down
14 changes: 5 additions & 9 deletions extensions/dockerhub/src/lib/hub/hub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
ExtensionsResponse,
ExtensionMetadata,
SearchParams,
FilterType as FilterTypeEnum,
SourceType,
} from "./types";
import { formatDate, formatSize, generateRepoURL } from "./utils";
import { ListAccessTokensResponse, AccessToken } from "./types";
Expand All @@ -25,7 +25,7 @@ const userURL = "/v2/user/";
const extensionsURL = "/v2/extensions";
const accessTokensURL = "/v2/access-tokens";

const searchURL = "https://hub.docker.com/api/content/v1/products/search";
const searchURL = "https://hub.docker.com/api/search/v3/catalog/search";
export const TwoFactorDetailMessage = "Require secondary authentication on MFA enabled account";

export class Hub {
Expand Down Expand Up @@ -163,22 +163,18 @@ export class Hub {
async search(params: SearchParams, signal?: AbortSignal): Promise<SearchResponse> {
const resp = await this.#client.get(searchURL, {
params,
headers: {
"Search-Version": "v3",
},
signal,
});
const res = resp.data as SearchResponse;
if (!res.summaries) {
if (!res.results) {
return res;
}
res.summaries = res.summaries?.map((summary) => {
if (summary.filter_type === FilterTypeEnum.OFFICIAL) {
res.results = res.results?.map((summary) => {
if (summary.source === SourceType.STORE) {
summary.url = `https://hub.docker.com/_/${summary.slug}`;
} else {
summary.url = `https://hub.docker.com/r/${summary.slug}`;
}
summary.from = summary.filter_type.replace("_", " ").toUpperCase();
return summary;
});
return res;
Expand Down
46 changes: 29 additions & 17 deletions extensions/dockerhub/src/lib/hub/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,28 @@ export interface ListTagsResponse {
results: Tag[];
}

export interface Summary {
interface RatePlanRepository {
name: string;
namespace: string;
description: string;
type: SearchTypeEnum;
pull_count: string;
is_automated: boolean;
is_official: boolean;
is_trusted: boolean;
last_pushed_at: string;
last_pulled_at: string;
archived: boolean;
}

interface RatePlan {
id: string;
repositories: RatePlanRepository[];
operating_systems: OperatingSystem[];
architectures: Architecture[];
}

export interface ImageSearchResult {
id: string;
name: string;
slug: string;
Expand All @@ -107,17 +128,11 @@ export interface Summary {
updated_at: string;
short_description: string;
source: SourceType;
popularity: number;
categories: unknown;
operating_systems: OperatingSystem[];
architectures: Architecture[];
categories: Record<string, string>[];
rate_plans: RatePlan[];
logo_url: LogoUrl;
certification_status: string;
star_count: number;
pull_count: string;
filter_type: FilterType;
url?: string;
from?: string;
}

export interface Publisher {
Expand Down Expand Up @@ -149,6 +164,7 @@ export enum SourceType {
STORE = "store",
VERIFIED_PUBLISHER = "verified_publisher",
COMMUNITY = "community",
OPEN_SOURCE = "open_source",
}

export enum FilterType {
Expand All @@ -159,12 +175,8 @@ export enum FilterType {
}

export interface SearchResponse {
count: number;
summaries: Summary[] | null;
page: number;
page_size: number;
next: string;
previous: string;
total: number;
results: ImageSearchResult[] | null;
}

export enum FilterTypes {
Expand All @@ -187,8 +199,8 @@ export enum ImageFilterEnum {
export interface SearchParams {
image_filter?: string;
operating_system?: string;
page_size: number;
q?: string;
size: number;
query?: string;
type?: SearchTypeEnum;
}

Expand Down
11 changes: 6 additions & 5 deletions extensions/dockerhub/src/lib/hub/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Image } from "@raycast/api";
import { SourceType } from "./types";

export const generateRepoURL = (namespace: string, name: string): string => {
return `https://hub.docker.com/repository/docker/${namespace}/${name}`;
Expand Down Expand Up @@ -48,20 +49,20 @@ export const formatSize = (size: number): string => {
return `${(size / (1024 * 1024 * 1024)).toFixed(2)}GB`;
};

export function mapFromToIcon(from: string | undefined):
export function mapFromToIcon(from: SourceType):
| Image.ImageLike
| {
value: Image.ImageLike;
tooltip: string;
} {
switch (from) {
case "OFFICIAL":
case SourceType.STORE:
return { value: { source: "official.png" }, tooltip: "Official" };
case "VERIFIED PUBLISHER":
case SourceType.VERIFIED_PUBLISHER:
return { value: { source: "verified.png" }, tooltip: "Verified Publisher" };
case "OPEN SOURCE":
case SourceType.OPEN_SOURCE:
return { value: { source: "oss.png" }, tooltip: "Open Source Program" };
case "COMMUNITY":
case SourceType.COMMUNITY:
return { value: { source: "docker-icon.png" }, tooltip: "Community" };
default:
return { source: "docker-icon.png" };
Expand Down

0 comments on commit 7b1da94

Please sign in to comment.