77 getBundledEnvInstallerPath ,
88 getBundledPythonEnvPath ,
99 getBundledPythonPath ,
10+ getLogFilePath ,
1011 installCondaPackEnvironment ,
1112 isBaseCondaEnv ,
1213 isEnvInstalledByDesktopApp ,
@@ -16,11 +17,16 @@ import {
1617import yargs from 'yargs/yargs' ;
1718import * as fs from 'fs' ;
1819import * as path from 'path' ;
19- import { appData } from './config/appdata' ;
20+ import { appData , ApplicationData } from './config/appdata' ;
2021import { IEnvironmentType , IPythonEnvironment } from './tokens' ;
21- import { SettingType , userSettings } from './config/settings' ;
22+ import {
23+ SettingType ,
24+ UserSettings ,
25+ userSettings ,
26+ WorkspaceSettings
27+ } from './config/settings' ;
2228import { Registry } from './registry' ;
23- import { app } from 'electron' ;
29+ import { app , shell } from 'electron' ;
2430import {
2531 condaEnvPathForCondaExePath ,
2632 getCondaChannels ,
@@ -188,6 +194,103 @@ export function parseCLIArgs(argv: string[]) {
188194 }
189195 }
190196 )
197+ . command (
198+ 'config <action>' ,
199+ 'Manage JupyterLab Desktop settings' ,
200+ yargs => {
201+ yargs
202+ . positional ( 'action' , {
203+ describe : 'Setting action' ,
204+ choices : [ 'list' , 'set' , 'unset' , 'open-file' ] ,
205+ default : 'list'
206+ } )
207+ . option ( 'project' , {
208+ describe : 'Set config for project at current working directory' ,
209+ type : 'boolean' ,
210+ default : false
211+ } )
212+ . option ( 'project-path' , {
213+ describe : 'Set / list config for project at specified path' ,
214+ type : 'string'
215+ } ) ;
216+ } ,
217+ async argv => {
218+ console . log ( 'Note: This is an experimental feature.' ) ;
219+
220+ const action = argv . action ;
221+ switch ( action ) {
222+ case 'list' :
223+ handleConfigListCommand ( argv ) ;
224+ break ;
225+ case 'set' :
226+ handleConfigSetCommand ( argv ) ;
227+ break ;
228+ case 'unset' :
229+ handleConfigUnsetCommand ( argv ) ;
230+ break ;
231+ case 'open-file' :
232+ handleConfigOpenFileCommand ( argv ) ;
233+ break ;
234+ default :
235+ console . log ( 'Invalid input for "config" command.' ) ;
236+ break ;
237+ }
238+ }
239+ )
240+ . command (
241+ 'appdata <action>' ,
242+ 'Manage JupyterLab Desktop app data' ,
243+ yargs => {
244+ yargs . positional ( 'action' , {
245+ describe : 'App data action' ,
246+ choices : [ 'list' , 'open-file' ] ,
247+ default : 'list'
248+ } ) ;
249+ } ,
250+ async argv => {
251+ console . log ( 'Note: This is an experimental feature.' ) ;
252+
253+ const action = argv . action ;
254+ switch ( action ) {
255+ case 'list' :
256+ handleAppDataListCommand ( argv ) ;
257+ break ;
258+ case 'open-file' :
259+ handleAppDataOpenFileCommand ( argv ) ;
260+ break ;
261+ default :
262+ console . log ( 'Invalid input for "appdata" command.' ) ;
263+ break ;
264+ }
265+ }
266+ )
267+ . command (
268+ 'logs <action>' ,
269+ 'Manage JupyterLab Desktop logs' ,
270+ yargs => {
271+ yargs . positional ( 'action' , {
272+ describe : 'Logs action' ,
273+ choices : [ 'show' , 'open-file' ] ,
274+ default : 'show'
275+ } ) ;
276+ } ,
277+ async argv => {
278+ console . log ( 'Note: This is an experimental feature.' ) ;
279+
280+ const action = argv . action ;
281+ switch ( action ) {
282+ case 'show' :
283+ handleLogsShowCommand ( argv ) ;
284+ break ;
285+ case 'open-file' :
286+ handleLogsOpenFileCommand ( argv ) ;
287+ break ;
288+ default :
289+ console . log ( 'Invalid input for "logs" command.' ) ;
290+ break ;
291+ }
292+ }
293+ )
191294 . parseAsync ( ) ;
192295}
193296
@@ -816,6 +919,266 @@ export async function handleEnvSetSystemPythonPathCommand(argv: any) {
816919 userSettings . save ( ) ;
817920}
818921
922+ function getProjectPathForConfigCommand ( argv : any ) : string | undefined {
923+ let projectPath = undefined ;
924+ if ( argv . project || argv . projectPath ) {
925+ projectPath = argv . projectPath
926+ ? path . resolve ( argv . projectPath )
927+ : process . cwd ( ) ;
928+ if (
929+ argv . projectPath &&
930+ ! ( fs . existsSync ( projectPath ) && fs . statSync ( projectPath ) . isDirectory ( ) )
931+ ) {
932+ console . error ( `Invalid project path! "${ projectPath } "` ) ;
933+ process . exit ( 1 ) ;
934+ }
935+ }
936+
937+ return projectPath ;
938+ }
939+
940+ function handleConfigListCommand ( argv : any ) {
941+ const listLines : string [ ] = [ ] ;
942+
943+ const projectPath = argv . projectPath
944+ ? path . resolve ( argv . projectPath )
945+ : process . cwd ( ) ;
946+
947+ listLines . push ( 'Project / Workspace settings' ) ;
948+ listLines . push ( '============================' ) ;
949+ listLines . push ( `[Project path: ${ projectPath } ]` ) ;
950+ listLines . push (
951+ `[Source file: ${ WorkspaceSettings . getWorkspaceSettingsPath ( projectPath ) } ]`
952+ ) ;
953+ listLines . push ( '\nSettings' ) ;
954+ listLines . push ( '========' ) ;
955+
956+ const wsSettings = new WorkspaceSettings ( projectPath ) . settings ;
957+ const wsSettingKeys = Object . keys ( wsSettings ) . sort ( ) ;
958+ if ( wsSettingKeys . length > 0 ) {
959+ for ( let key of wsSettingKeys ) {
960+ const value = wsSettings [ key ] . value ;
961+ listLines . push ( `${ key } : ${ JSON . stringify ( value ) } ` ) ;
962+ }
963+ } else {
964+ listLines . push ( 'No setting overrides found in project directory.' ) ;
965+ }
966+ listLines . push ( '\n' ) ;
967+
968+ listLines . push ( 'Global settings' ) ;
969+ listLines . push ( '===============' ) ;
970+ listLines . push ( `[Source file: ${ UserSettings . getUserSettingsPath ( ) } ]` ) ;
971+ listLines . push ( '\nSettings' ) ;
972+ listLines . push ( '========' ) ;
973+
974+ const settingKeys = Object . values ( SettingType ) . sort ( ) ;
975+ const settings = userSettings . settings ;
976+
977+ for ( let key of settingKeys ) {
978+ const setting = settings [ key ] ;
979+ listLines . push (
980+ `${ key } : ${ JSON . stringify ( setting . value ) } [${
981+ setting . differentThanDefault ? 'modified' : 'set to default'
982+ } ${ setting . wsOverridable ? ', project overridable' : '' } ]`
983+ ) ;
984+ }
985+
986+ console . log ( listLines . join ( '\n' ) ) ;
987+ }
988+
989+ function handleConfigSetCommand ( argv : any ) {
990+ const parseSetting = ( ) : { key : string ; value : string } => {
991+ if ( argv . _ . length !== 3 ) {
992+ console . error ( `Invalid setting. Use "set <settingKey> <value>" format.` ) ;
993+ return { key : undefined , value : undefined } ;
994+ }
995+
996+ let value ;
997+
998+ // boolean, arrays, objects
999+ try {
1000+ value = JSON . parse ( argv . _ [ 2 ] ) ;
1001+ } catch ( error ) {
1002+ try {
1003+ // string without quotes
1004+ value = JSON . parse ( `"${ argv . _ [ 2 ] } "` ) ;
1005+ } catch ( error ) {
1006+ console . error ( error . message ) ;
1007+ }
1008+ }
1009+
1010+ return { key : argv . _ [ 1 ] , value : value } ;
1011+ } ;
1012+
1013+ const projectPath = getProjectPathForConfigCommand ( argv ) ;
1014+
1015+ let key , value ;
1016+ try {
1017+ const keyVal = parseSetting ( ) ;
1018+ key = keyVal . key ;
1019+ value = keyVal . value ;
1020+ } catch ( error ) {
1021+ console . error ( 'Failed to parse setting!' ) ;
1022+ return ;
1023+ }
1024+
1025+ if ( key === undefined || value === undefined ) {
1026+ console . error ( 'Failed to parse key value pair!' ) ;
1027+ return ;
1028+ }
1029+
1030+ if ( ! ( key in SettingType ) ) {
1031+ console . error ( `Invalid setting key! "${ key } "` ) ;
1032+ return ;
1033+ }
1034+
1035+ if ( projectPath ) {
1036+ const setting = userSettings . settings [ key ] ;
1037+ if ( ! setting . wsOverridable ) {
1038+ console . error ( `Setting "${ key } " is not overridable by project.` ) ;
1039+ return ;
1040+ }
1041+
1042+ const wsSettings = new WorkspaceSettings ( projectPath ) ;
1043+ wsSettings . setValue ( key as SettingType , value ) ;
1044+ wsSettings . save ( ) ;
1045+ } else {
1046+ userSettings . setValue ( key as SettingType , value ) ;
1047+ userSettings . save ( ) ;
1048+ }
1049+
1050+ console . log (
1051+ `${
1052+ projectPath ? 'Project' : 'Global'
1053+ } setting "${ key } " set to "${ value } " successfully.`
1054+ ) ;
1055+ }
1056+
1057+ function handleConfigUnsetCommand ( argv : any ) {
1058+ const parseKey = ( ) : string => {
1059+ if ( argv . _ . length !== 2 ) {
1060+ console . error ( `Invalid setting. Use "unset <settingKey>" format.` ) ;
1061+ return undefined ;
1062+ }
1063+
1064+ return argv . _ [ 1 ] ;
1065+ } ;
1066+
1067+ const projectPath = getProjectPathForConfigCommand ( argv ) ;
1068+
1069+ let key = parseKey ( ) ;
1070+
1071+ if ( ! key ) {
1072+ return ;
1073+ }
1074+
1075+ if ( ! ( key in SettingType ) ) {
1076+ console . error ( `Invalid setting key! "${ key } "` ) ;
1077+ return ;
1078+ }
1079+
1080+ if ( projectPath ) {
1081+ const setting = userSettings . settings [ key ] ;
1082+ if ( ! setting . wsOverridable ) {
1083+ console . error ( `Setting "${ key } " is not overridable by project.` ) ;
1084+ return ;
1085+ }
1086+
1087+ const wsSettings = new WorkspaceSettings ( projectPath ) ;
1088+ wsSettings . unsetValue ( key as SettingType ) ;
1089+ wsSettings . save ( ) ;
1090+ } else {
1091+ userSettings . unsetValue ( key as SettingType ) ;
1092+ userSettings . save ( ) ;
1093+ }
1094+
1095+ console . log (
1096+ `${ projectPath ? 'Project' : 'Global' } setting "${ key } " reset to ${
1097+ projectPath ? 'global ' : ''
1098+ } default successfully.`
1099+ ) ;
1100+ }
1101+
1102+ function handleConfigOpenFileCommand ( argv : any ) {
1103+ const projectPath = getProjectPathForConfigCommand ( argv ) ;
1104+ const settingsFilePath = projectPath
1105+ ? WorkspaceSettings . getWorkspaceSettingsPath ( projectPath )
1106+ : UserSettings . getUserSettingsPath ( ) ;
1107+
1108+ console . log ( `Settings file path: ${ settingsFilePath } ` ) ;
1109+
1110+ if (
1111+ ! ( fs . existsSync ( settingsFilePath ) && fs . statSync ( settingsFilePath ) . isFile ( ) )
1112+ ) {
1113+ console . log ( 'Settings file does not exist!' ) ;
1114+ return ;
1115+ }
1116+
1117+ shell . openPath ( settingsFilePath ) ;
1118+ }
1119+
1120+ function handleAppDataListCommand ( argv : any ) {
1121+ const listLines : string [ ] = [ ] ;
1122+
1123+ listLines . push ( 'Application data' ) ;
1124+ listLines . push ( '================' ) ;
1125+ listLines . push ( `[Source file: ${ ApplicationData . getAppDataPath ( ) } ]` ) ;
1126+ listLines . push ( '\nData' ) ;
1127+ listLines . push ( '====' ) ;
1128+
1129+ const skippedKeys = new Set ( [ 'newsList' ] ) ;
1130+ const appDataKeys = Object . keys ( appData ) . sort ( ) ;
1131+
1132+ for ( let key of appDataKeys ) {
1133+ if ( key . startsWith ( '_' ) || skippedKeys . has ( key ) ) {
1134+ continue ;
1135+ }
1136+ const data = ( appData as any ) [ key ] ;
1137+ listLines . push ( `${ key } : ${ JSON . stringify ( data ) } ` ) ;
1138+ }
1139+
1140+ console . log ( listLines . join ( '\n' ) ) ;
1141+ }
1142+
1143+ function handleAppDataOpenFileCommand ( argv : any ) {
1144+ const appDataFilePath = ApplicationData . getAppDataPath ( ) ;
1145+ console . log ( `App data file path: ${ appDataFilePath } ` ) ;
1146+
1147+ if (
1148+ ! ( fs . existsSync ( appDataFilePath ) && fs . statSync ( appDataFilePath ) . isFile ( ) )
1149+ ) {
1150+ console . log ( 'App data file does not exist!' ) ;
1151+ return ;
1152+ }
1153+
1154+ shell . openPath ( appDataFilePath ) ;
1155+ }
1156+
1157+ function handleLogsShowCommand ( argv : any ) {
1158+ const logFilePath = getLogFilePath ( ) ;
1159+ console . log ( `Log file path: ${ logFilePath } ` ) ;
1160+
1161+ if ( ! ( fs . existsSync ( logFilePath ) && fs . statSync ( logFilePath ) . isFile ( ) ) ) {
1162+ console . log ( 'Log file does not exist!' ) ;
1163+ return ;
1164+ }
1165+
1166+ const logs = fs . readFileSync ( logFilePath ) ;
1167+ console . log ( logs . toString ( ) ) ;
1168+ }
1169+
1170+ function handleLogsOpenFileCommand ( argv : any ) {
1171+ const logFilePath = getLogFilePath ( ) ;
1172+ console . log ( `Log file path: ${ logFilePath } ` ) ;
1173+
1174+ if ( ! ( fs . existsSync ( logFilePath ) && fs . statSync ( logFilePath ) . isFile ( ) ) ) {
1175+ console . log ( 'Log file does not exist!' ) ;
1176+ return ;
1177+ }
1178+
1179+ shell . openPath ( logFilePath ) ;
1180+ }
1181+
8191182export async function launchCLIinEnvironment (
8201183 envPath : string
8211184) : Promise < boolean > {
0 commit comments