@@ -86,15 +86,15 @@ export async function promptImportGithubDatabase(
8686 maxStep : 2
8787 } ) ;
8888 const githubRepo = await window . showInputBox ( {
89- title : 'Enter a GitHub repository in the format < owner>/<repo> (e.g. github/codeql)' ,
90- placeHolder : '<owner>/<repo>' ,
89+ title : 'Enter a GitHub repository URL or "name with owner" (e.g. https://github.com/github/codeql or github/codeql)' ,
90+ placeHolder : 'https://github.com/<owner>/<repo> or <owner>/<repo>' ,
9191 ignoreFocusOut : true ,
9292 } ) ;
9393 if ( ! githubRepo ) {
9494 return ;
9595 }
9696
97- if ( ! REPO_REGEX . test ( githubRepo ) ) {
97+ if ( ! looksLikeGithubRepo ( githubRepo ) ) {
9898 throw new Error ( `Invalid GitHub repository: ${ githubRepo } ` ) ;
9999 }
100100
@@ -465,18 +465,62 @@ export async function findDirWithFile(
465465 return ;
466466}
467467
468+ /**
469+ * The URL pattern is https://github.com/{owner}/{name}/{subpages}.
470+ *
471+ * This function accepts any URL that matches the pattern above. It also accepts just the
472+ * name with owner (NWO): `<owner>/<repo>`.
473+ *
474+ * @param githubRepo The GitHub repository URL or NWO
475+ *
476+ * @return true if this looks like a valid GitHub repository URL or NWO
477+ */
478+ export function looksLikeGithubRepo (
479+ githubRepo : string | undefined
480+ ) : githubRepo is string {
481+ if ( ! githubRepo ) {
482+ return false ;
483+ }
484+ if ( REPO_REGEX . test ( githubRepo ) || convertGitHubUrlToNwo ( githubRepo ) ) {
485+ return true ;
486+ }
487+ return false ;
488+ }
489+
490+ /**
491+ * Converts a GitHub repository URL to the corresponding NWO.
492+ * @param githubUrl The GitHub repository URL
493+ * @return The corresponding NWO, or undefined if the URL is not valid
494+ */
495+ function convertGitHubUrlToNwo ( githubUrl : string ) : string | undefined {
496+ try {
497+ const uri = Uri . parse ( githubUrl , true ) ;
498+ if ( uri . scheme !== 'https' ) {
499+ return ;
500+ }
501+ if ( uri . authority !== 'github.com' && uri . authority !== 'www.github.com' ) {
502+ return ;
503+ }
504+ const paths = uri . path . split ( '/' ) . filter ( ( segment : string ) => segment ) ;
505+ const nwo = `${ paths [ 0 ] } /${ paths [ 1 ] } ` ;
506+ if ( REPO_REGEX . test ( nwo ) ) {
507+ return nwo ;
508+ }
509+ return ;
510+ } catch ( e ) {
511+ // Ignore the error here, since we catch failures at a higher level.
512+ // In particular: returning undefined leads to an error in 'promptImportGithubDatabase'.
513+ return ;
514+ }
515+ }
516+
468517export async function convertGithubNwoToDatabaseUrl (
469518 githubRepo : string ,
470519 credentials : Credentials ,
471520 progress : ProgressCallback ) : Promise < string | undefined > {
472521 try {
473- // TODO: In future, we could accept GitHub URLs in addition to NWOs.
474- // Similar to "looksLikeLgtmUrl".
475- if ( ! REPO_REGEX . test ( githubRepo ) ) {
476- throw new Error ( 'Invalid repository format. Must be in the format <owner>/<repo> (e.g. github/codeql)' ) ;
477- }
478-
479- const [ owner , repo ] = githubRepo . split ( '/' ) ;
522+ const nwo = convertGitHubUrlToNwo ( githubRepo ) || githubRepo ;
523+ const [ owner , repo ] = nwo . split ( '/' ) ;
480524
481525 const octokit = await credentials . getOctokit ( ) ;
482526 const response = await octokit . request ( 'GET /repos/:owner/:repo/code-scanning/codeql/databases' , { owner, repo } ) ;
@@ -531,7 +575,7 @@ export function looksLikeLgtmUrl(lgtmUrl: string | undefined): lgtmUrl is string
531575 return false ;
532576 }
533577
534- const paths = uri . path . split ( '/' ) . filter ( ( segment ) => segment ) ;
578+ const paths = uri . path . split ( '/' ) . filter ( ( segment : string ) => segment ) ;
535579 return paths . length >= 4 && paths [ 0 ] === 'projects' ;
536580 } catch ( e ) {
537581 return false ;
@@ -604,7 +648,7 @@ export async function convertLgtmUrlToDatabaseUrl(
604648async function downloadLgtmProjectMetadata ( lgtmUrl : string ) : Promise < any > {
605649 const uri = Uri . parse ( lgtmUrl , true ) ;
606650 const paths = [ 'api' , 'v1.0' ] . concat (
607- uri . path . split ( '/' ) . filter ( ( segment ) => segment )
651+ uri . path . split ( '/' ) . filter ( ( segment : string ) => segment )
608652 ) . slice ( 0 , 6 ) ;
609653 const projectUrl = `https://lgtm.com/${ paths . join ( '/' ) } ` ;
610654 const projectResponse = await fetch ( projectUrl ) ;
0 commit comments