diff --git a/src/service/providers/ImageLinkProvider.ts b/src/service/providers/ImageLinkProvider.ts index 7a1c3bd..4bd6daa 100644 --- a/src/service/providers/ImageLinkProvider.ts +++ b/src/service/providers/ImageLinkProvider.ts @@ -51,43 +51,67 @@ export class ImageLinkProvider extends ProviderBase): { uri: string, start: number, length: number } | undefined { - let match: RegExpExecArray | null; - let namespace: string | undefined; - let imageName: string | undefined; - - if ((match = dockerHubImageRegex.exec(image)) && - (imageName = match.groups?.imageName)) { - - imageTypes.add('dockerHub'); - - return { - uri: `https://hub.docker.com/_/${imageName}`, - start: match.index, - length: imageName.length - }; - } else if ((match = dockerHubNamespacedImageRegex.exec(image)) && - (namespace = match.groups?.namespace) && - (imageName = match.groups?.imageName)) { - - imageTypes.add('dockerHubNamespaced'); - - return { - uri: `https://hub.docker.com/r/${namespace}/${imageName}`, - start: match.index, - length: namespace.length + 1 + imageName.length // 1 is the length of the '/' after namespace - }; - } else if ((match = mcrImageRegex.exec(image)) && - (namespace = match.groups?.namespace?.replace(/\/$/, '')) && - (imageName = match.groups?.imageName)) { - - imageTypes.add('mcr'); - - return { - uri: `https://hub.docker.com/_/microsoft-${namespace.replace('/', '-')}-${imageName}`, - start: match.index, - length: 18 + namespace.length + 1 + imageName.length // 18 is the length of 'mcr.microsoft.com/', 1 is the length of the '/' after namespace - }; + const parts = image.split('/'); + + if (parts.length === 1) { + let match: RegExpExecArray | null; + let imageName: string | undefined; + if ((match = dockerHubImageRegex.exec(image)) && + (imageName = match.groups?.imageName)) { + + imageTypes.add('dockerHub'); + + return { + uri: `https://hub.docker.com/_/${imageName}`, + start: match.index, + length: imageName.length + }; + } + return undefined; + } + + const firstPart = parts[0]; + const isRegistry = firstPart.includes('.') || firstPart.includes(':') || firstPart === 'localhost'; + + if (isRegistry) { + if (firstPart === 'mcr.microsoft.com') { + let match: RegExpExecArray | null; + let namespace: string | undefined; + let imageName: string | undefined; + if ((match = mcrImageRegex.exec(image)) && + (namespace = match.groups?.namespace?.replace(/\/$/, '')) && + (imageName = match.groups?.imageName)) { + + imageTypes.add('mcr'); + + return { + uri: `https://hub.docker.com/_/microsoft-${namespace.replace('/', '-')}-${imageName}`, + start: match.index, + length: 18 + namespace.length + 1 + imageName.length // 18 is the length of 'mcr.microsoft.com/', 1 is the length of the '/' after namespace + }; + } + } + return undefined; } + + if (parts.length === 2) { + let match: RegExpExecArray | null; + let namespace: string | undefined; + let imageName: string | undefined; + if ((match = dockerHubNamespacedImageRegex.exec(image)) && + (namespace = match.groups?.namespace) && + (imageName = match.groups?.imageName)) { + + imageTypes.add('dockerHubNamespaced'); + + return { + uri: `https://hub.docker.com/r/${namespace}/${imageName}`, + start: match.index, + length: namespace.length + 1 + imageName.length // 1 is the length of the '/' after namespace + }; + } + } + return undefined; } } diff --git a/src/test/providers/ImageLinkProvider.test.ts b/src/test/providers/ImageLinkProvider.test.ts index 58e54fe..541d691 100644 --- a/src/test/providers/ImageLinkProvider.test.ts +++ b/src/test/providers/ImageLinkProvider.test.ts @@ -162,6 +162,12 @@ services: b: { image: 'foo:1234/alpine' }, + c: { + image: 'localhost/alpine' + }, + d: { + image: 'nrt.vultrcr.com/wulicoco/code-sync' + }, } }; diff --git a/src/vscode/AlternateYamlLanguageServiceClientFeature.ts b/src/vscode/AlternateYamlLanguageServiceClientFeature.ts index 2094a97..41f8d59 100644 --- a/src/vscode/AlternateYamlLanguageServiceClientFeature.ts +++ b/src/vscode/AlternateYamlLanguageServiceClientFeature.ts @@ -33,7 +33,7 @@ export class AlternateYamlLanguageServiceClientFeature implements StaticFeature, basicCompletions: redhat || docker, advancedCompletions: false, // The other extensions do not have advanced completions for Compose docs hover: redhat || docker, // Compose spec has descriptions - imageLinks: docker, // Docker's extension supports Docker Hub, GHCR, MAR, and Quay.io + imageLinks: false, // Keep Compose image links local so private registries aren't treated as Docker Hub images serviceStartupCodeLens: false, // The other extensions do not provide any code lens formatting: false, // The other extensions do support formatting, but we enable it regardless so that an explicitly-chosen formatter always works };