fix: improved import from docker parsing (#3642)
This commit is contained in:
@@ -509,19 +509,19 @@ interface ReleasesRepositoryImport extends ReleasesRepository {
|
|||||||
alreadyImported: boolean;
|
alreadyImported: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ContainerImageSelectorProps {
|
interface ImportRepositorySelectProps {
|
||||||
containerImage: ReleasesRepositoryImport;
|
repository: ReleasesRepositoryImport;
|
||||||
integration?: Integration;
|
integration?: Integration;
|
||||||
versionFilterPrecisionOptions: string[];
|
versionFilterPrecisionOptions: string[];
|
||||||
onImageSelectionChanged?: (isSelected: boolean) => void;
|
onImageSelectionChanged?: (isSelected: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContainerImageSelector = ({
|
const ImportRepositorySelect = ({
|
||||||
containerImage,
|
repository,
|
||||||
integration,
|
integration,
|
||||||
versionFilterPrecisionOptions,
|
versionFilterPrecisionOptions,
|
||||||
onImageSelectionChanged,
|
onImageSelectionChanged,
|
||||||
}: ContainerImageSelectorProps) => {
|
}: ImportRepositorySelectProps) => {
|
||||||
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
const tRepository = useScopedI18n("widget.releases.option.repositories");
|
||||||
const checkBoxProps: CheckboxProps = !onImageSelectionChanged
|
const checkBoxProps: CheckboxProps = !onImageSelectionChanged
|
||||||
? {
|
? {
|
||||||
@@ -539,29 +539,29 @@ const ContainerImageSelector = ({
|
|||||||
label={
|
label={
|
||||||
<Group>
|
<Group>
|
||||||
<Image
|
<Image
|
||||||
src={containerImage.iconUrl}
|
src={repository.iconUrl}
|
||||||
style={{
|
style={{
|
||||||
height: "1.2em",
|
height: "1.2em",
|
||||||
width: "1.2em",
|
width: "1.2em",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Text>{containerImage.identifier}</Text>
|
<Text>{repository.identifier}</Text>
|
||||||
</Group>
|
</Group>
|
||||||
}
|
}
|
||||||
{...checkBoxProps}
|
{...checkBoxProps}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{containerImage.versionFilter && (
|
{repository.versionFilter && (
|
||||||
<Group gap={5}>
|
<Group gap={5}>
|
||||||
<Text c="dimmed" size="xs">
|
<Text c="dimmed" size="xs">
|
||||||
{tRepository("versionFilter.label")}:
|
{tRepository("versionFilter.label")}:
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Code>{containerImage.versionFilter.prefix && containerImage.versionFilter.prefix}</Code>
|
<Code>{repository.versionFilter.prefix && repository.versionFilter.prefix}</Code>
|
||||||
<Code color="var(--mantine-primary-color-light)" fw={700}>
|
<Code color="var(--mantine-primary-color-light)" fw={700}>
|
||||||
{versionFilterPrecisionOptions[containerImage.versionFilter.precision]}
|
{versionFilterPrecisionOptions[repository.versionFilter.precision]}
|
||||||
</Code>
|
</Code>
|
||||||
<Code>{containerImage.versionFilter.suffix && containerImage.versionFilter.suffix}</Code>
|
<Code>{repository.versionFilter.suffix && repository.versionFilter.suffix}</Code>
|
||||||
</Group>
|
</Group>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
@@ -610,36 +610,47 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
enabled: innerProps.isAdmin,
|
enabled: innerProps.isAdmin,
|
||||||
});
|
});
|
||||||
|
|
||||||
const containersImages: ReleasesRepositoryImport[] = useMemo(
|
const importRepositories: ReleasesRepositoryImport[] = useMemo(
|
||||||
() =>
|
() =>
|
||||||
docker.data?.containers.reduce<ReleasesRepositoryImport[]>((acc, containerImage) => {
|
docker.data?.containers.reduce<ReleasesRepositoryImport[]>((acc, container) => {
|
||||||
const imageParts = containerImage.image.split("/");
|
const [maybeSource, maybeIdentifierAndVersion] = container.image.split(/\/(.*)/);
|
||||||
const source = imageParts.length > 1 ? imageParts[0] : "docker.io";
|
const hasSource = maybeSource && maybeSource in sourceToProviderKind;
|
||||||
const identifierImage = imageParts.length > 1 ? imageParts[1] : imageParts[0];
|
const source = hasSource ? maybeSource : "docker.io";
|
||||||
|
const identifierAndVersion = hasSource ? maybeIdentifierAndVersion : container.image;
|
||||||
|
|
||||||
if (!source || !identifierImage) return acc;
|
if (!identifierAndVersion) return acc;
|
||||||
|
|
||||||
const providerKey = source in containerImageToProviderKind ? containerImageToProviderKind[source] : "dockerHub";
|
const providerKey = sourceToProviderKind[source];
|
||||||
const integrationId = Object.values(innerProps.integrations).find(
|
const integrationId = Object.values(innerProps.integrations).find(
|
||||||
(integration) => integration.kind === providerKey,
|
(integration) => integration.kind === providerKey,
|
||||||
)?.id;
|
)?.id;
|
||||||
|
|
||||||
const [identifier, version] = identifierImage.split(":");
|
const [identifier, version] = identifierAndVersion.split(":");
|
||||||
|
|
||||||
if (!identifier || !integrationId) return acc;
|
if (!identifier || !integrationId) return acc;
|
||||||
|
|
||||||
if (acc.some((item) => item.providerIntegrationId === integrationId && item.identifier === identifier))
|
if (
|
||||||
|
acc.some(
|
||||||
|
(item) =>
|
||||||
|
item.providerIntegrationId !== undefined &&
|
||||||
|
innerProps.integrations[item.providerIntegrationId]?.kind === providerKey &&
|
||||||
|
item.identifier === identifier,
|
||||||
|
)
|
||||||
|
)
|
||||||
return acc;
|
return acc;
|
||||||
|
|
||||||
acc.push({
|
acc.push({
|
||||||
id: createId(),
|
id: createId(),
|
||||||
providerIntegrationId: integrationId,
|
providerIntegrationId: integrationId,
|
||||||
identifier,
|
identifier,
|
||||||
iconUrl: containerImage.iconUrl ?? undefined,
|
iconUrl: container.iconUrl ?? undefined,
|
||||||
name: formatIdentifierName(identifier),
|
name: formatIdentifierName(identifier),
|
||||||
versionFilter: version ? parseImageVersionToVersionFilter(version) : undefined,
|
versionFilter: version ? parseImageVersionToVersionFilter(version) : undefined,
|
||||||
alreadyImported: innerProps.repositories.some(
|
alreadyImported: innerProps.repositories.some(
|
||||||
(item) => item.providerIntegrationId === integrationId && item.identifier === identifier,
|
(item) =>
|
||||||
|
item.providerIntegrationId !== undefined &&
|
||||||
|
innerProps.integrations[item.providerIntegrationId]?.kind === providerKey &&
|
||||||
|
item.identifier === identifier,
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
return acc;
|
return acc;
|
||||||
@@ -657,13 +668,13 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
}, [innerProps, selectedImages, actions]);
|
}, [innerProps, selectedImages, actions]);
|
||||||
|
|
||||||
const allImagesImported = useMemo(
|
const allImagesImported = useMemo(
|
||||||
() => containersImages.every((containerImage) => containerImage.alreadyImported),
|
() => importRepositories.every((repository) => repository.alreadyImported),
|
||||||
[containersImages],
|
[importRepositories],
|
||||||
);
|
);
|
||||||
|
|
||||||
const anyImagesImported = useMemo(
|
const anyImagesImported = useMemo(
|
||||||
() => containersImages.some((containerImage) => containerImage.alreadyImported),
|
() => importRepositories.some((repository) => repository.alreadyImported),
|
||||||
[containersImages],
|
[importRepositories],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -673,7 +684,7 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
<Loader size="xl" />
|
<Loader size="xl" />
|
||||||
<Title order={3}>{tRepository("importRepositories.loading")}</Title>
|
<Title order={3}>{tRepository("importRepositories.loading")}</Title>
|
||||||
</Stack>
|
</Stack>
|
||||||
) : containersImages.length === 0 ? (
|
) : importRepositories.length === 0 ? (
|
||||||
<Stack justify="center" align="center">
|
<Stack justify="center" align="center">
|
||||||
<IconBrandDocker stroke={1} size={128} />
|
<IconBrandDocker stroke={1} size={128} />
|
||||||
<Title order={3}>{tRepository("importRepositories.noImagesFound")}</Title>
|
<Title order={3}>{tRepository("importRepositories.noImagesFound")}</Title>
|
||||||
@@ -694,23 +705,23 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
</Accordion.Control>
|
</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
{!allImagesImported &&
|
{!allImagesImported &&
|
||||||
containersImages
|
importRepositories
|
||||||
.filter((containerImage) => !containerImage.alreadyImported)
|
.filter((repository) => !repository.alreadyImported)
|
||||||
.map((containerImage) => {
|
.map((repository) => {
|
||||||
const integration = containerImage.providerIntegrationId
|
const integration = repository.providerIntegrationId
|
||||||
? innerProps.integrations[containerImage.providerIntegrationId]
|
? innerProps.integrations[repository.providerIntegrationId]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContainerImageSelector
|
<ImportRepositorySelect
|
||||||
key={containerImage.id}
|
key={repository.id}
|
||||||
containerImage={containerImage}
|
repository={repository}
|
||||||
integration={integration}
|
integration={integration}
|
||||||
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
||||||
onImageSelectionChanged={(isSelected) =>
|
onImageSelectionChanged={(isSelected) =>
|
||||||
isSelected
|
isSelected
|
||||||
? setSelectedImages([...selectedImages, containerImage])
|
? setSelectedImages([...selectedImages, repository])
|
||||||
: setSelectedImages(selectedImages.filter((img) => img !== containerImage))
|
: setSelectedImages(selectedImages.filter((img) => img !== repository))
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -723,17 +734,17 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
</Accordion.Control>
|
</Accordion.Control>
|
||||||
<Accordion.Panel>
|
<Accordion.Panel>
|
||||||
{anyImagesImported &&
|
{anyImagesImported &&
|
||||||
containersImages
|
importRepositories
|
||||||
.filter((containerImage) => containerImage.alreadyImported)
|
.filter((repository) => repository.alreadyImported)
|
||||||
.map((containerImage) => {
|
.map((repository) => {
|
||||||
const integration = containerImage.providerIntegrationId
|
const integration = repository.providerIntegrationId
|
||||||
? innerProps.integrations[containerImage.providerIntegrationId]
|
? innerProps.integrations[repository.providerIntegrationId]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContainerImageSelector
|
<ImportRepositorySelect
|
||||||
key={containerImage.id}
|
key={repository.id}
|
||||||
containerImage={containerImage}
|
repository={repository}
|
||||||
integration={integration}
|
integration={integration}
|
||||||
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
versionFilterPrecisionOptions={innerProps.versionFilterPrecisionOptions}
|
||||||
/>
|
/>
|
||||||
@@ -763,7 +774,7 @@ const RepositoryImportModal = createModal<RepositoryImportProps>(({ innerProps,
|
|||||||
size: "xl",
|
size: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
const containerImageToProviderKind: Record<string, IntegrationKind> = {
|
const sourceToProviderKind: Record<string, IntegrationKind> = {
|
||||||
"ghcr.io": "github",
|
"ghcr.io": "github",
|
||||||
"docker.io": "dockerHub",
|
"docker.io": "dockerHub",
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user