import * as vscode from 'vscode' ; import * as nls from 'vscode-nls' ; import { API as GitAPI , Repository } from './typings/git' ; import { getOctokit } from './auth' ; import { TextEncoder } from 'util' ; import { basename } from 'path' ; import { Octokit } from '@octokit/rest' ; const localize = nls . loadMessageBundle ( ) ; function sanitizeRepositoryName ( value ) { return value . trim ( ) . replace ( /[^a-z0-9_.]/ig , '-' ) ; } function getPick < T extends vscode . QuickPickItem > ( quickpick ) { return Promise . race < T | undefined > ( [ new Promise < T > ( c => quickpick . onDidAccept ( ( ) => quickpick . selectedItems . length > 0 && c ( quickpick . selectedItems [ 0 ] ) ) ) , new Promise < undefined > ( c => quickpick . onDidHide ( ( ) => c ( undefined ) ) ) ] ) ; } export async function publishRepository ( gitAPI , repository ? ) { if ( ! vscode . workspace . workspaceFolders ?. length ) { return ; } let folder ; if ( repository ) { folder = repository . rootUri ; } else if ( gitAPI . repositories . length === 1 ) { repository = gitAPI . repositories [ 0 ] ; folder = repository . rootUri ; } else if ( vscode . workspace . workspaceFolders . length === 1 ) { folder = vscode . workspace . workspaceFolders [ 0 ] . uri ; } else { const picks = vscode . workspace . workspaceFolders . map ( folder => ( { label : folder . name , folder } ) ) ; const placeHolder = localize ( 'pick folder' , "Pick a folder to publish to GitHub" ) ; const pick = await vscode . window . showQuickPick ( picks , { placeHolder } ) ; if ( ! pick ) { return ; } folder = pick . folder . uri ; } let quickpick = vscode . window . createQuickPick < vscode . QuickPickItem & { repo ? : string ; auth ? : 'https' | 'ssh' ; isPrivate ? : boolean ; } > ( ) ; quickpick . ignoreFocusOut = true ; quickpick . placeholder = 'Repository Name' ; quickpick . value = basename ( folder . fsPath ) ; quickpick . show ( ) ; quickpick . busy = true ; let owner ; let octokit ; try { octokit = await getOctokit ( ) ; const user = await octokit . users . getAuthenticated ( { } ) ; owner = user . data . login ; } catch ( e ) { quickpick . dispose ( ) ; return ; } quickpick . busy = false ; let repo ; let isPrivate ; const onDidChangeValue = async ( ) => { const sanitizedRepo = sanitizeRepositoryName ( quickpick . value ) ; if ( ! sanitizedRepo ) { quickpick . items = [ ] ; } else { quickpick . items = [ { label : `$(repo) Publish to GitHub private repository` , description : `$(github) ${ owner }/${ sanitizedRepo }` , alwaysShow : true , repo : sanitizedRepo , isPrivate : true } , { label : `$(repo) Publish to GitHub public repository` , description : `$(github) ${ owner }/${ sanitizedRepo }` , alwaysShow : true , repo : sanitizedRepo , isPrivate : false } , ] ; } } ; onDidChangeValue ( ) ; while ( true ) { const listener = quickpick . onDidChangeValue ( onDidChangeValue ) ; const pick = await getPick ( quickpick ) ; listener . dispose ( ) ; repo = pick ?. repo ; isPrivate = pick ?. isPrivate ?? true ; if ( repo ) { try { quickpick . busy = true ; await octokit . repos . get ( { owner , repo : repo } ) ; quickpick . items = [ { label : `$(error) GitHub repository already exists` , description : `$(github) ${ owner }/${ repo }` , alwaysShow : true } ] ; } catch { break ; } finally { quickpick . busy = false ; } } } quickpick . dispose ( ) ; if ( ! repo ) { return ; } if ( ! repository ) { const gitignore = vscode . Uri . joinPath ( folder , '.gitignore' ) ; let shouldGenerateGitignore = false ; try { await vscode . workspace . fs . stat ( gitignore ) ; } catch ( err ) { shouldGenerateGitignore = true ; } if ( shouldGenerateGitignore ) { quickpick = vscode . window . createQuickPick ( ) ; quickpick . placeholder = localize ( 'ignore' , "Select which files should be included in the repository." ) ; quickpick . canSelectMany = true ; quickpick . show ( ) ; try { quickpick . busy = true ; const children = ( await vscode . workspace . fs . readDirectory ( folder ) ) . map ( ( [ name ] ) => name ) . filter ( name => name !== '.git' ) ; quickpick . items = children . map ( name => ( { label : name } ) ) ; quickpick . selectedItems = quickpick . items ; quickpick . busy = false ; const result = await Promise . race ( [ new Promise < readonly vscode . QuickPickItem [ ] > ( c => quickpick . onDidAccept ( ( ) => c ( quickpick . selectedItems ) ) ) , new Promise < undefined > ( c => quickpick . onDidHide ( ( ) => c ( undefined ) ) ) ] ) ; if ( ! result || result . length === 0 ) { return ; } const ignored = new Set ( children ) ; result . forEach ( c => ignored . delete ( c . label ) ) ; if ( ignored . size > 0 ) { const raw = [ ... ignored ] . map ( i => `/${ i }` ) . join ( '\n' ) ; const encoder = new TextEncoder ( ) ; await vscode . workspace . fs . writeFile ( gitignore , encoder . encode ( raw ) ) ; } } finally { quickpick . dispose ( ) ; } } } const githubRepository = await vscode . window . withProgress ( { location : vscode . ProgressLocation . Notification , cancellable : false , title : 'Publish to GitHub' } , async ( progress ) => { progress . report ( { message : isPrivate ? localize ( 'publishing_private' , "Publishing to a private GitHub repository" ) : localize ( 'publishing_public' , "Publishing to a public GitHub repository" ) , increment : 25 } ) ; const res = await octokit . repos . createForAuthenticatedUser ( { name : repo ! , private : isPrivate } ) ; const createdGithubRepository = res . data ; progress . report ( { message : localize ( 'publishing_firstcommit' , "Creating first commit" ) , increment : 25 } ) ; if ( ! repository ) { repository = await gitAPI . init ( folder ) || undefined ; if ( ! repository ) { return ; } await repository . commit ( 'first commit' , { all : true } ) ; } progress . report ( { message : localize ( 'publishing_uploading' , "Uploading files" ) , increment : 25 } ) ; const branch = await repository . getBranch ( 'HEAD' ) ; await repository . addRemote ( 'origin' , createdGithubRepository . clone_url ) ; await repository . push ( 'origin' , branch . name , true ) ; return createdGithubRepository ; } ) ; if ( ! githubRepository ) { return ; } const openOnGitHub = localize ( 'openingithub' , "Open on GitHub" ) ; vscode . window . showInformationMessage ( localize ( 'publishing_done' , "Successfully published the '{0}' repository to GitHub." , `${ owner }/${ repo }` ) , openOnGitHub ) . then ( action => { if ( action === openOnGitHub ) { vscode . commands . executeCommand ( 'vscode.open' , vscode . Uri . parse ( githubRepository . html_url ) ) ; } } ) ; }