7
7
getBundledEnvInstallerPath ,
8
8
getBundledPythonEnvPath ,
9
9
getBundledPythonPath ,
10
+ getLogFilePath ,
10
11
installCondaPackEnvironment ,
11
12
isBaseCondaEnv ,
12
13
isEnvInstalledByDesktopApp ,
@@ -16,11 +17,16 @@ import {
16
17
import yargs from 'yargs/yargs' ;
17
18
import * as fs from 'fs' ;
18
19
import * as path from 'path' ;
19
- import { appData } from './config/appdata' ;
20
+ import { appData , ApplicationData } from './config/appdata' ;
20
21
import { 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' ;
22
28
import { Registry } from './registry' ;
23
- import { app } from 'electron' ;
29
+ import { app , shell } from 'electron' ;
24
30
import {
25
31
condaEnvPathForCondaExePath ,
26
32
getCondaChannels ,
@@ -188,6 +194,103 @@ export function parseCLIArgs(argv: string[]) {
188
194
}
189
195
}
190
196
)
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
+ )
191
294
. parseAsync ( ) ;
192
295
}
193
296
@@ -816,6 +919,266 @@ export async function handleEnvSetSystemPythonPathCommand(argv: any) {
816
919
userSettings . save ( ) ;
817
920
}
818
921
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
+
819
1182
export async function launchCLIinEnvironment (
820
1183
envPath : string
821
1184
) : Promise < boolean > {
0 commit comments