88import * as workspaceInstance from '@gitpod/gitpod-protocol/lib/workspace-instance' ;
99import * as grpc from '@grpc/grpc-js' ;
1010import * as fs from 'fs' ;
11+ import * as os from 'os' ;
12+ import * as uuid from 'uuid' ;
1113import { GitpodPluginModel , GitpodExtensionContext , setupGitpodContext , registerTasks , registerIpcHookCli } from 'gitpod-shared' ;
1214import { GetTokenRequest } from '@gitpod/supervisor-api-grpc/lib/token_pb' ;
1315import { PortsStatus , ExposedPortInfo , PortsStatusRequest , PortsStatusResponse , PortAutoExposure , PortVisibility , OnPortExposedAction } from '@gitpod/supervisor-api-grpc/lib/status_pb' ;
@@ -19,7 +21,9 @@ import * as path from 'path';
1921import { URL } from 'url' ;
2022import * as util from 'util' ;
2123import * as vscode from 'vscode' ;
22- import { ThrottledDelayer } from './async' ;
24+ import { ThrottledDelayer } from './util/async' ;
25+ import { download } from './util/download' ;
26+ import { getManifest } from './util/extensionManagmentUtill' ;
2327
2428let gitpodContext : GitpodExtensionContext | undefined ;
2529export async function activate ( context : vscode . ExtensionContext ) {
@@ -185,7 +189,7 @@ export function registerAuth(context: GitpodExtensionContext): void {
185189 if ( ! userResponse . ok ) {
186190 throw new Error ( `Getting GitHub account info failed: ${ userResponse . statusText } ` ) ;
187191 }
188- const user : { id : string , login : string } = await userResponse . json ( ) ;
192+ const user = await ( userResponse . json ( ) as Promise < { id : string , login : string } > ) ;
189193 return {
190194 id : user . id ,
191195 accountName : user . login
@@ -774,7 +778,7 @@ interface IOpenVSXQueryResult {
774778 extensions : IOpenVSXExtensionsMetadata [ ] ;
775779}
776780
777- async function validateExtensions ( extensionsToValidate : { id : string , version ?: string } [ ] , token : vscode . CancellationToken ) {
781+ async function validateExtensions ( extensionsToValidate : { id : string , version ?: string } [ ] , linkToValidate : string [ ] , token : vscode . CancellationToken ) {
778782 const allUserExtensions = vscode . extensions . all . filter ( ext => ! ext . packageJSON [ 'isBuiltin' ] && ! ext . packageJSON [ 'isUserBuiltin' ] ) ;
779783
780784 const lookup = new Set < string > ( extensionsToValidate . map ( ( { id } ) => id ) ) ;
@@ -795,7 +799,8 @@ async function validateExtensions(extensionsToValidate: { id: string, version?:
795799 return {
796800 extensions : [ ] ,
797801 missingMachined : [ ] ,
798- uninstalled : [ ]
802+ uninstalled : [ ] ,
803+ links : [ ]
799804 } ;
800805 }
801806 }
@@ -806,21 +811,21 @@ async function validateExtensions(extensionsToValidate: { id: string, version?:
806811 `${ process . env . VSX_REGISTRY_URL || 'https://open-vsx.org' } /api/-/query` ,
807812 {
808813 method : 'POST' ,
809- timeout : 5000 ,
810814 headers : {
811815 'Content-Type' : 'application/json' ,
812816 'Accept' : 'application/json'
813817 } ,
814818 body : JSON . stringify ( {
815819 extensionId : id
816- } )
820+ } ) ,
821+ timeout : 2000
817822 }
818823 ) . then ( resp => {
819824 if ( ! resp . ok ) {
820825 console . error ( 'Failed to query open-vsx while validating gitpod.yml' ) ;
821826 return undefined ;
822827 }
823- return resp . json ( ) ;
828+ return resp . json ( ) as Promise < IOpenVSXQueryResult > ;
824829 } , e => {
825830 console . error ( 'Fetch failed while querying open-vsx' , e ) ;
826831 return undefined ;
@@ -837,17 +842,40 @@ async function validateExtensions(extensionsToValidate: { id: string, version?:
837842 return {
838843 extensions : [ ] ,
839844 missingMachined : [ ] ,
840- uninstalled : [ ]
845+ uninstalled : [ ] ,
846+ links : [ ]
841847 } ;
842848 }
843849 }
844850
845- // TODO: validate links
851+ const links = new Set < string > ( ) ;
852+ for ( const link of linkToValidate ) {
853+ const downloadPath = path . join ( os . tmpdir ( ) , uuid . v4 ( ) ) ;
854+ try {
855+ await download ( link , downloadPath , token , 10000 ) ;
856+ const manifest = await getManifest ( downloadPath ) ;
857+ if ( manifest . engines ?. vscode ) {
858+ links . add ( link ) ;
859+ }
860+ } catch ( error ) {
861+ console . error ( 'Failed to validate vsix url' , error ) ;
862+ }
863+
864+ if ( token . isCancellationRequested ) {
865+ return {
866+ extensions : [ ] ,
867+ missingMachined : [ ] ,
868+ uninstalled : [ ] ,
869+ links : [ ]
870+ } ;
871+ }
872+ }
846873
847874 return {
848875 extensions : [ ...validatedExtensions ] ,
849876 missingMachined : [ ...missingMachined ] ,
850- uninstalled : [ ...uninstalled ]
877+ uninstalled : [ ...uninstalled ] ,
878+ links : [ ...links ]
851879 } ;
852880}
853881
@@ -926,10 +954,7 @@ export function registerExtensionManagement(context: GitpodExtensionContext): vo
926954 }
927955 try {
928956 const toLink = new Map < string , vscode . Range > ( ) ;
929- const toFind = new Map < string , {
930- version ?: string ,
931- range : vscode . Range
932- } > ( ) ;
957+ const toFind = new Map < string , { version ?: string , range : vscode . Range } > ( ) ;
933958 let document : vscode . TextDocument | undefined ;
934959 try {
935960 document = await vscode . workspace . openTextDocument ( gitpodFileUri ) ;
@@ -994,7 +1019,8 @@ export function registerExtensionManagement(context: GitpodExtensionContext): vo
9941019 }
9951020
9961021 const extensionsToValidate = [ ...toFind . entries ( ) ] . map ( ( [ id , { version } ] ) => ( { id, version } ) ) ;
997- const result = await validateExtensions ( extensionsToValidate , token ) ;
1022+ const linksToValidate = [ ...toLink . keys ( ) ] ;
1023+ const result = await validateExtensions ( extensionsToValidate , linksToValidate , token ) ;
9981024
9991025 if ( token . isCancellationRequested ) {
10001026 return ;
@@ -1016,14 +1042,14 @@ export function registerExtensionManagement(context: GitpodExtensionContext): vo
10161042 pushDiagnostic ( diagnostic ) ;
10171043 }
10181044
1019- // for (const link of result.links) {
1020- // toLink.delete(link);
1021- // }
1022- // for (const [link, range] of toLink) {
1023- // const diagnostic = new vscode.Diagnostic(range, link + invalidVSIXLinkMessageSuffix, vscode.DiagnosticSeverity.Error);
1024- // diagnostic.source = 'gitpod';
1025- // pushDiagnostic(diagnostic);
1026- // }
1045+ for ( const link of result . links ) {
1046+ toLink . delete ( link ) ;
1047+ }
1048+ for ( const [ link , range ] of toLink ) {
1049+ const diagnostic = new vscode . Diagnostic ( range , link + invalidVSIXLinkMessageSuffix , vscode . DiagnosticSeverity . Error ) ;
1050+ diagnostic . source = 'gitpod' ;
1051+ pushDiagnostic ( diagnostic ) ;
1052+ }
10271053
10281054 for ( const id of result . missingMachined ) {
10291055 const diagnostic = new vscode . Diagnostic ( new vscode . Range ( new vscode . Position ( 0 , 0 ) , new vscode . Position ( 0 , 1 ) ) , id + missingExtensionMessageSuffix , vscode . DiagnosticSeverity . Warning ) ;
@@ -1158,13 +1184,9 @@ export function registerExtensionManagement(context: GitpodExtensionContext): vo
11581184 return codeActions ;
11591185 }
11601186 } ) ) ;
1187+
11611188 validateGitpodFile ( ) ;
11621189 context . subscriptions . push ( gitpodDiagnostics ) ;
1163- context . subscriptions . push ( vscode . workspace . onDidChangeTextDocument ( e => {
1164- if ( e . document . uri . toString ( ) === gitpodFileUri . toString ( ) ) {
1165- validateGitpodFile ( ) ;
1166- }
1167- } ) ) ;
11681190 const gitpodFileWatcher = vscode . workspace . createFileSystemWatcher ( gitpodFileUri . fsPath ) ;
11691191 context . subscriptions . push ( gitpodFileWatcher ) ;
11701192 context . subscriptions . push ( gitpodFileWatcher . onDidCreate ( ( ) => validateGitpodFile ( ) ) ) ;
0 commit comments