import * as grpc from '@grpc/grpc-js' ; import * as fs from 'graceful-fs' ; import * as vscode from 'vscode' ; import { BzlAssetDownloader } from './download' ; import path = require ( 'path' ) ; import normalize = require ( 'normalize-path' ) ; import { Settings } from './settings' ; import { ProtoGrpcType as BzlProtoType } from '../proto/bzl' ; import { ProtoGrpcType as CodesearchProtoType } from '../proto/codesearch' ; import { getGRPCCredentials , loadBzlProtos , loadCodesearchProtos } from './proto' ; import { ConfigurationContext } from '../common' ; /**
 * Configuration for a generic component.
 */ export interface ComponentConfiguration { enabled : boolean ; } /**
 * Configuration for the bzl server.
 */ export interface BzlConfiguration extends ComponentConfiguration { autoLaunch : boolean ; release : string ; downloadBaseURL : string ; executable : string ; address : vscode . Uri ; command : string [ ] ; creds : grpc . ChannelCredentials ; bzpb : BzlProtoType ; cspb : CodesearchProtoType ; } /**
 * Configuration for the license server.
 */ export interface SubscriptionConfiguration extends ComponentConfiguration { serverAddress : vscode . Uri ; token : string | undefined ; } /**
 * Configuration for the bzl server.
 */ export interface BazelConfiguration extends ComponentConfiguration { executable : string | undefined ; buildFlags : string [ ] ; starlarkDebugFlags : string [ ] ; testFlags : string [ ] ; runFlags : string [ ] ; } /**
 * Configuration for the remote cache.
 */ export interface RemoteCacheConfiguration extends ComponentConfiguration { autoLaunch : boolean ; executable : string | undefined ; command : string [ ] ; maxSizeGb : number ; address : vscode . Uri ; dir : string | undefined ; } /**
 * Configuration for the bes backend.
 */ export interface BuildEventServiceConfiguration extends ComponentConfiguration { backendAddress : vscode . Uri ; frontendAddress : vscode . Uri ; } /**
 * Configuration for codesearch.
 */ export interface CodeSearchConfiguration extends ComponentConfiguration { defaultLinesContext : number ; maxMatches : number ; foldCase : boolean ; defaultUseRegexp : boolean ; } /**
 * Configuration for invocations.
 */ export interface InvocationsConfiguration extends ComponentConfiguration { invokeWithBuildEventStreaming : boolean ; buildEventPublishAllActions : boolean ; hideOutputPanelOnSuccess : boolean ; } export interface LanguageServerConfiguration extends ComponentConfiguration { executable : string ; command : string [ ] ; enableCodelenses : boolean ; enableCodelensCopyLabel : boolean ; enableCodelensCodesearch : boolean ; enableCodelensBrowse : boolean ; enableCodelensStarlarkDebug : boolean ; enableCodelensBuild : boolean ; enableCodelensTest : boolean ; enableCodelensRun : boolean ; } export interface StarlarkDebuggerConfiguration extends ComponentConfiguration { autoLaunch : boolean ; debugAdapterExecutable : string ; debugAdapterCommand : string [ ] ; debugAdapterHost : string ; debugAdapterPort : number ; debugAdapterVerbosity : number ; debugServerHost : string | undefined ; debugServerPort : number | undefined ; } export class BazelSettings extends Settings < BazelConfiguration > { constructor ( configCtx , section ) { super ( configCtx , section ) ; } protected async configure ( config ) { const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , executable : config . get < string | undefined > ( 'executable' ) , buildFlags : config . get < string [ ] > ( 'buildFlags' , [ ] ) , testFlags : config . get < string [ ] > ( 'testFlags' , [ ] ) , starlarkDebugFlags : config . get < string [ ] > ( 'starlarkDebugFlags' , [ '--experimental_skylark_debug' , '--experimental_skylark_debug_verbose_logging' , ] ) , runFlags : config . get < string [ ] > ( 'runFlags' , [ ] ) , } ; return cfg ; } } export class InvocationsSettings extends Settings < InvocationsConfiguration > { constructor ( configCtx , section , private subscription ) { super ( configCtx , section ) ; } protected async configure ( config ) { const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , invokeWithBuildEventStreaming : config . get < boolean > ( 'invokeWithBuildEventStreaming' , true ) , buildEventPublishAllActions : config . get < boolean > ( 'buildEventPublishAllActions' , true ) , hideOutputPanelOnSuccess : config . get < boolean > ( 'hideOutputPanelOnSuccess' , true ) , } ; const subscription = await this . subscription . get ( ) ; if ( ! subscription . token ) { cfg . invokeWithBuildEventStreaming = false ; } return cfg ; } } export class CodeSearchSettings extends Settings < CodeSearchConfiguration > { constructor ( configCtx , section ) { super ( configCtx , section ) ; } protected async configure ( config ) { const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , maxMatches : config . get < number > ( 'maxMatches' , 50 ) , foldCase : config . get < boolean > ( 'foldCase' , true ) , defaultLinesContext : config . get < number > ( 'defaultLinesContext' , 3 ) , defaultUseRegexp : config . get < boolean > ( 'defaultUseRegexp' , false ) , } ; return cfg ; } } export class StarlarkDebuggerSettings extends Settings < StarlarkDebuggerConfiguration > { constructor ( configCtx , section , private bzl ) { super ( configCtx , section ) ; } protected async configure ( config ) { const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , autoLaunch : config . get < boolean > ( 'autoLaunch' , false ) , debugAdapterExecutable : config . get < string > ( 'debugAdapterExecutable' , '' ) , debugAdapterCommand : config . get < string [ ] > ( 'debugAdapterCommand' , [ 'debug' , 'adapter' ] ) , debugAdapterHost : config . get < string > ( 'debugAdapterHost' , 'localhost' ) , debugAdapterPort : config . get < number > ( 'debugAdapterPort' , 4711 ) , debugAdapterVerbosity : config . get < number > ( 'debugServerVerbosity' , 1 ) , debugServerHost : config . get < string | undefined > ( 'debugServerHost' ) , debugServerPort : config . get < number | undefined > ( 'debugServerPort' ) , } ; if ( ! cfg . debugAdapterExecutable ) { const bzl = await this . bzl . get ( ) ; cfg . debugAdapterExecutable = bzl . executable ; } return cfg ; } } export class BzlSettings extends Settings < BzlConfiguration > { constructor ( configCtx , section , private ctx , private bazel ) { super ( configCtx , section ) ; this . disposables . push ( bazel . onDidConfigurationChange ( ( ) => this . reconfigure . bind ( this ) ) ) ; } protected async configure ( config ) { const bazel = await this . bazel . get ( ) ; const address = vscode . Uri . parse ( config . get < string > ( 'address' , 'grpc://localhost:8085' ) ) ; const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , autoLaunch : config . get < boolean > ( 'autoLaunch' , true ) , downloadBaseURL : config . get < string > ( 'downloadBaseUrl' , 'https://get.bzl.io' ) , release : config . get < string > ( 'release' , 'v1.3.19' ) , executable : normalize ( config . get < string > ( 'executable' , '' ) ) , address : address , command : config . get < string [ ] > ( 'command' , [ 'serve' , '--address=${address}' ] ) , creds : getGRPCCredentials ( address . authority ) , bzpb : loadBzlProtos ( this . configCtx . protoFile ( 'bzl.proto' ) . fsPath ) , cspb : loadCodesearchProtos ( this . configCtx . protoFile ( 'codesearch.proto' ) . fsPath ) , } ; if ( ! cfg . executable ) { await setServerExecutable ( this . ctx , cfg ) ; } return cfg ; } } export class SubscriptionSettings extends Settings < SubscriptionConfiguration > { constructor ( configCtx , section , private ctx ) { super ( configCtx , section ) ; } protected async configure ( config ) { const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , serverAddress : vscode . Uri . parse ( config . get < string > ( 'serverAddress' , 'grpcs://accounts.bzl.io:443' ) ) , token : config . get < string | undefined > ( 'token' ) , } ; if ( ! cfg . token ) { const legacy = vscode . workspace . getConfiguration ( 'bsv.bzl.license' ) ; cfg . token = legacy . get < string | undefined > ( 'token' ) ; } await setAccountToken ( this . ctx , cfg ) ; return cfg ; } } export class RemoteCacheSettings extends Settings < RemoteCacheConfiguration > { constructor ( configCtx , section , private bzl ) { super ( configCtx , section ) ; this . disposables . push ( bzl . onDidConfigurationChange ( ( ) => this . reconfigure . bind ( this ) ) ) ; } protected async configure ( config ) { const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , address : vscode . Uri . parse ( config . get < string > ( 'address' , 'grpc://localhost:2020' ) ) , command : config . get < string [ ] > ( 'command' , [ 'cache' ] ) , dir : config . get < string | undefined > ( 'dir' , undefined ) , executable : config . get < string | undefined > ( 'executable' ) , maxSizeGb : config . get < number > ( 'maxSizeGb' , 10 ) , autoLaunch : config . get < boolean > ( 'autoLaunch' , true ) , } ; if ( ! cfg . executable ) { const bzl = await this . bzl . get ( ) ; cfg . executable = bzl . executable ; } cfg . executable = normalize ( cfg . executable ) ; return cfg ; } } export class BuildEventServiceSettings extends Settings < BuildEventServiceConfiguration > { constructor ( configCtx , section , private bzl ) { super ( configCtx , section ) ; this . disposables . push ( bzl . onDidConfigurationChange ( ( ) => this . reconfigure . bind ( this ) ) ) ; } protected async configure ( config ) { const addr = config . get < string | undefined > ( 'address' ) ; if ( addr ) { return { enabled : config . get < boolean > ( 'enabled' , true ) , backendAddress : vscode . Uri . parse ( `grpc://${ addr }` ) , frontendAddress : vscode . Uri . parse ( `http://${ addr }/pipeline` ) , } ; } else { const bzl = await this . bzl . get ( ) ; return { enabled : config . get < boolean > ( 'enabled' , true ) , backendAddress : bzl . address , frontendAddress : vscode . Uri . parse ( `http://${ bzl . address . authority }/pipeline` ) , } ; } } } export class LanguageServerSettings extends Settings < LanguageServerConfiguration > { constructor ( configCtx , section , private bzl , private subscription ) { super ( configCtx , section ) ; this . disposables . push ( bzl . onDidConfigurationChange ( ( ) => this . reconfigure . bind ( this ) ) ) ; } protected async configure ( config ) { const bzl = await this . bzl . get ( ) ; const cfg = { enabled : config . get < boolean > ( 'enabled' , true ) , executable : bzl . executable , command : config . get < string [ ] > ( 'command' , [ 'lsp' , 'serve' , '--log_level=info' ] ) , enableCodelenses : config . get < boolean > ( 'enableCodelenses' , true ) , enableCodelensCopyLabel : config . get < boolean > ( 'enableCodelensCopyLabel' , true ) , enableCodelensCodesearch : config . get < boolean > ( 'enableCodelensCodesearch' , true ) , enableCodelensBrowse : config . get < boolean > ( 'enableCodelensBrowse' , true ) , enableCodelensStarlarkDebug : config . get < boolean > ( 'enableCodelensBrowse' , true ) , enableCodelensBuild : config . get < boolean > ( 'enableCodelensBuild' , true ) , enableCodelensTest : config . get < boolean > ( 'enableCodelensTest' , true ) , enableCodelensRun : config . get < boolean > ( 'enableCodelensRun' , true ) , } ; cfg . command . push ( `--address=${ bzl . address }` ) ; const subscription = await this . subscription . get ( ) ; if ( ! subscription . token ) { cfg . enableCodelensCodesearch = false ; cfg . enableCodelensBrowse = false ; } return cfg ; } } export async function setAccountToken ( ctx , subscription ) { if ( subscription . token ) { return ; } const uri = getLicenseFileURI ( ) ; if ( ! fs . existsSync ( uri . fsPath ) ) { return ; } const token = fs . readFileSync ( uri . fsPath ) ; subscription . token = token . toString ( ) . trim ( ) ; } export function getLicenseFileURI ( ext = '' ) { const homedir = require ( 'os' ) . homedir ( ) ; const name = 'license.key' + ext ; return vscode . Uri . file ( path . join ( homedir , '.bzl' , name ) ) ; } export async function writeLicenseFile ( token ) { const uri = getLicenseFileURI ( ) ; if ( fs . existsSync ( uri . fsPath ) ) { const backup = getLicenseFileURI ( '.' + Date . now ( ) ) ; fs . copyFileSync ( uri . fsPath , backup . fsPath ) ; } fs . writeFileSync ( uri . fsPath , token ) ; } export async function setServerExecutable ( ctx , server ) { try { const fileUri = await maybeInstallExecutable ( ctx , server ) ; server . executable = normalize ( fileUri . fsPath ) ; } catch ( e ) { throw new Error ( `could not install bzl: ${ e instanceof Error ? e . message : e }` ) ; } if ( ! fs . existsSync ( server . executable ) ) { throw new Error ( `could not activate: bzl executable file "${ server . executable }" not found.` ) ; } } param cfg param storagePath export async function maybeInstallExecutable ( ctx , cfg ) { const cancellationTokenSource = new vscode . CancellationTokenSource ( ) ; const cancellationToken = cancellationTokenSource . token ; const downloader = await BzlAssetDownloader . fromConfiguration ( cfg ) ; const mode = 0o755 ; return downloader . getOrDownloadFile ( ctx , mode , cancellationToken ) ; }