@learnpack/learnpack 2.1.50 → 2.1.52

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,320 +1,320 @@
1
- // import path from "path";
2
- import { flags } from "@oclif/command"
3
-
4
- import SessionCommand from "../utils/SessionCommand"
5
- import Console from "../utils/console"
6
- import socket from "../managers/socket"
7
- import TelemetryManager, { TStep } from "../managers/telemetry"
8
- import createServer from "../managers/server"
9
- import queue from "../utils/fileQueue"
10
- import {
11
- decompress,
12
- downloadEditor,
13
- checkIfDirectoryExists,
14
- } from "../managers/file"
15
- import { prioritizeHTMLFile } from "../utils/misc"
16
-
17
- import { IGitpodData } from "../models/gitpod-data"
18
- import { IExercise, IExerciseData } from "../models/exercise-obj"
19
- import { eventManager } from "../utils/osOperations"
20
- import { cli } from "cli-ux"
21
- import { TOpenWindowData } from "../models/socket"
22
- import {
23
- checkNotInstalledPlugins,
24
- checkNotInstalledDependencies,
25
- } from "../utils/checkNotInstalled"
26
-
27
- export default class StartCommand extends SessionCommand {
28
- static description = "Runs a small server with all the exercise instructions"
29
-
30
- static flags = {
31
- ...SessionCommand.flags,
32
- port: flags.string({ char: "p", description: "server port" }),
33
- host: flags.string({ char: "h", description: "server host" }),
34
- disableGrading: flags.boolean({
35
- char: "D",
36
- description: "disble grading functionality",
37
- default: false,
38
- }),
39
- // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
40
- watch: flags.boolean({
41
- char: "w",
42
- description: "Watch for file changes",
43
- default: false,
44
- }),
45
- editor: flags.string({
46
- char: "e",
47
- description: "[preview, extension]",
48
- options: ["extension", "preview"],
49
- }),
50
- version: flags.string({
51
- char: "v",
52
- description: "E.g: 1.0.1",
53
- default: undefined,
54
- }),
55
- grading: flags.string({
56
- char: "g",
57
- description: "[isolated, incremental]",
58
- options: ["isolated", "incremental"],
59
- }),
60
- debug: flags.boolean({
61
- char: "d",
62
- description: "debugger mode for more verbage",
63
- default: false,
64
- }),
65
- }
66
-
67
- // 🛑 IMPORTANT
68
- // Every command that will use the configManager needs this init method
69
- async init() {
70
- const { flags } = this.parse(StartCommand)
71
- await this.initSession(flags)
72
- }
73
-
74
- async run() {
75
- // get configuration object
76
- const configObject = this.configManager?.get()
77
-
78
- // TODO: console.log(this.config.platform); get the platfrom from Oclif
79
- const hasXDG = await eventManager.checkXDGInstalled()
80
-
81
- const installedPlugins = this.config.plugins.map(plugin => {
82
- return `${plugin.pjson.name}`
83
- })
84
-
85
- if (configObject) {
86
- const { config } = configObject
87
- // build exerises
88
- this.configManager?.buildIndex()
89
-
90
- Console.debug(
91
- `Grading: ${config?.grading} ${
92
- config?.disabledActions?.includes("test") ? "(disabled)" : ""
93
- }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
94
- Array.isArray(configObject?.exercises) ?
95
- configObject?.exercises.length :
96
- 0
97
- } exercises found`
98
- )
99
-
100
- const neededPlugins = await checkNotInstalledPlugins(
101
- configObject?.exercises || [],
102
- installedPlugins,
103
- this
104
- )
105
-
106
- const allDepsInstalled = await checkNotInstalledDependencies(
107
- neededPlugins.needed
108
- )
109
- if (!allDepsInstalled) {
110
- this.exit(1)
111
- }
112
-
113
- const appAlreadyExists = checkIfDirectoryExists(
114
- `${config?.dirPath}/_app`
115
- )
116
-
117
- if (!appAlreadyExists) {
118
- // download app and decompress
119
- await downloadEditor(
120
- config?.editor.version,
121
- `${config?.dirPath}/app.tar.gz`
122
- )
123
-
124
- Console.info("Decompressing LearnPack UI, this may take a minute...")
125
- await decompress(
126
- `${config?.dirPath}/app.tar.gz`,
127
- `${config?.dirPath}/_app/`
128
- )
129
- }
130
-
131
- // listen to socket commands
132
- if (config && this.configManager) {
133
- const server = await createServer(
134
- configObject,
135
- this.configManager,
136
- process.env.NODE_ENV === "test"
137
- )
138
- server.setMaxListeners(25)
139
-
140
- // I should call a method to get the EventListener
141
- const dispatcher = queue.dispatcher({
142
- create: true,
143
- path: `${config.dirPath}/vscode_queue.json`,
144
- })
145
-
146
- if (configObject.exercises) {
147
- const agent = configObject.config?.editor.agent || ""
148
- const path = configObject.config?.dirPath || ""
149
- const tutorialSlug = configObject.config?.slug || ""
150
-
151
- const steps = configObject.exercises.map(
152
- (e: IExercise, index): TStep => ({
153
- slug: e.slug,
154
- position: e.position || index,
155
- files: e.files,
156
- ai_interactions: [],
157
- compilations: [],
158
- tests: [],
159
- is_testeable: e.graded || false,
160
- })
161
- )
162
- if (path && steps.length > 0) {
163
- TelemetryManager.start(agent, steps, path, tutorialSlug)
164
- }
165
-
166
- if (config.telemetry) {
167
- TelemetryManager.urls = config.telemetry
168
- }
169
- }
170
-
171
- socket.start(config, server, false)
172
-
173
- socket.on("open", (data: IGitpodData) => {
174
- Console.debug("Opening these files: ", data)
175
-
176
- const files = prioritizeHTMLFile(data.files)
177
-
178
- if (config.editor.agent === "os") {
179
- eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
180
- } else {
181
- dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
182
- }
183
-
184
- socket.ready("Ready to compile...")
185
- })
186
-
187
- socket.on("open_window", (data: TOpenWindowData) => {
188
- Console.debug("Opening window: ", data)
189
- // cli.open(data.url); This uses XDG under the ground
190
- if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
191
- eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
192
- } else {
193
- dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
194
- }
195
-
196
- socket.log("open_window", "", undefined, data.url)
197
- })
198
-
199
- socket.on("open_terminal", (exercise: IExerciseData) => {
200
- Console.debug("Opening terminal: ", exercise)
201
- // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
202
- if (config.editor.agent === "vscode") {
203
- dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise)
204
- }
205
- })
206
-
207
- socket.on("reset", (exercise: IExerciseData) => {
208
- try {
209
- this.configManager?.reset(exercise.exerciseSlug)
210
- socket.ready("Ready to compile...")
211
- } catch (error) {
212
- socket.error(
213
- "compiler-error",
214
- (error as TypeError).message ||
215
- "There was an error reseting the exercise"
216
- )
217
- setTimeout(() => socket.ready("Ready to compile..."), 2000)
218
- }
219
- })
220
-
221
- socket.on("build", async (data: IExerciseData) => {
222
- const exercise = this.configManager?.getExercise(data.exerciseSlug)
223
-
224
- if (!exercise?.language) {
225
- socket.error(
226
- "compiler-error",
227
- "Impossible to detect language to build for " +
228
- data.exerciseSlug +
229
- "..."
230
- )
231
- return
232
- }
233
-
234
- socket.log(
235
- "compiling",
236
- "Building exercise " +
237
- data.exerciseSlug +
238
- " with " +
239
- exercise.language +
240
- "..."
241
- )
242
- await this.config.runHook("action", {
243
- action: "compile",
244
- socket,
245
- configuration: config,
246
- exercise,
247
- telemetry: TelemetryManager,
248
- })
249
- })
250
-
251
- socket.on("ai_interaction", (data: any) => {
252
- const { stepPosition, event, eventData } = data
253
- TelemetryManager.registerStepEvent(stepPosition, event, eventData)
254
- })
255
-
256
- socket.on("test", async (data: IExerciseData) => {
257
- const exercise = this.configManager?.getExercise(data.exerciseSlug)
258
-
259
- if (!exercise?.language) {
260
- socket.error(
261
- "compiler-error",
262
- "Impossible to detect engine language for testing for " +
263
- data.exerciseSlug +
264
- "..."
265
- )
266
- return
267
- }
268
-
269
- if (
270
- config?.disabledActions!.includes("test") ||
271
- config?.disableGrading
272
- ) {
273
- socket.ready("Grading is disabled on configuration")
274
- return true
275
- }
276
-
277
- socket.log(
278
- "testing",
279
- "Testing your exercise using the " + exercise.language + " engine."
280
- )
281
-
282
- await this.config.runHook("action", {
283
- action: "test",
284
- socket,
285
- configuration: config,
286
- exercise,
287
- telemetry: TelemetryManager,
288
- })
289
-
290
- this.configManager?.save()
291
-
292
- return true
293
- })
294
-
295
- const terminate = async () => {
296
- Console.error("Terminating Learnpack...")
297
- await TelemetryManager.submit()
298
- this.configManager?.noCurrentExercise()
299
- dispatcher.enqueue(dispatcher.events.END)
300
- process.exit()
301
- }
302
-
303
- server.on("close", terminate)
304
- process.on("SIGINT", terminate)
305
- process.on("SIGTERM", terminate)
306
- process.on("SIGHUP", terminate)
307
-
308
- // finish the server startup
309
- setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000)
310
-
311
- // start watching for file changes
312
- if (StartCommand.flags.watch)
313
- this.configManager.watchIndex(_filename => {
314
- // Instead of reloading with socket.reload(), I just notify the frontend for the file change
315
- socket.emit("file_change", "ready", _filename)
316
- })
317
- }
318
- }
319
- }
320
- }
1
+ // import path from "path";
2
+ import { flags } from "@oclif/command"
3
+
4
+ import SessionCommand from "../utils/SessionCommand"
5
+ import Console from "../utils/console"
6
+ import socket from "../managers/socket"
7
+ import TelemetryManager, { TStep } from "../managers/telemetry"
8
+ import createServer from "../managers/server"
9
+ import queue from "../utils/fileQueue"
10
+ import {
11
+ decompress,
12
+ downloadEditor,
13
+ checkIfDirectoryExists,
14
+ } from "../managers/file"
15
+ import { prioritizeHTMLFile } from "../utils/misc"
16
+
17
+ import { IGitpodData } from "../models/gitpod-data"
18
+ import { IExercise, IExerciseData } from "../models/exercise-obj"
19
+ import { eventManager } from "../utils/osOperations"
20
+ import { cli } from "cli-ux"
21
+ import { TOpenWindowData } from "../models/socket"
22
+ import {
23
+ checkNotInstalledPlugins,
24
+ checkNotInstalledDependencies,
25
+ } from "../utils/checkNotInstalled"
26
+
27
+ export default class StartCommand extends SessionCommand {
28
+ static description = "Runs a small server with all the exercise instructions"
29
+
30
+ static flags = {
31
+ ...SessionCommand.flags,
32
+ port: flags.string({ char: "p", description: "server port" }),
33
+ host: flags.string({ char: "h", description: "server host" }),
34
+ disableGrading: flags.boolean({
35
+ char: "D",
36
+ description: "disble grading functionality",
37
+ default: false,
38
+ }),
39
+ // disableGrading: flags.boolean({char: 'dg', description: 'disble grading functionality', default: false }),
40
+ watch: flags.boolean({
41
+ char: "w",
42
+ description: "Watch for file changes",
43
+ default: false,
44
+ }),
45
+ editor: flags.string({
46
+ char: "e",
47
+ description: "[preview, extension]",
48
+ options: ["extension", "preview"],
49
+ }),
50
+ version: flags.string({
51
+ char: "v",
52
+ description: "E.g: 1.0.1",
53
+ default: undefined,
54
+ }),
55
+ grading: flags.string({
56
+ char: "g",
57
+ description: "[isolated, incremental]",
58
+ options: ["isolated", "incremental"],
59
+ }),
60
+ debug: flags.boolean({
61
+ char: "d",
62
+ description: "debugger mode for more verbage",
63
+ default: false,
64
+ }),
65
+ }
66
+
67
+ // 🛑 IMPORTANT
68
+ // Every command that will use the configManager needs this init method
69
+ async init() {
70
+ const { flags } = this.parse(StartCommand)
71
+ await this.initSession(flags)
72
+ }
73
+
74
+ async run() {
75
+ // get configuration object
76
+ const configObject = this.configManager?.get()
77
+
78
+ // TODO: console.log(this.config.platform); get the platfrom from Oclif
79
+ const hasXDG = await eventManager.checkXDGInstalled()
80
+
81
+ const installedPlugins = this.config.plugins.map(plugin => {
82
+ return `${plugin.pjson.name}`
83
+ })
84
+
85
+ if (configObject) {
86
+ const { config } = configObject
87
+ // build exerises
88
+ this.configManager?.buildIndex()
89
+
90
+ Console.debug(
91
+ `Grading: ${config?.grading} ${
92
+ config?.disabledActions?.includes("test") ? "(disabled)" : ""
93
+ }, editor: ${config?.editor.mode} ${config?.editor.version}, for ${
94
+ Array.isArray(configObject?.exercises) ?
95
+ configObject?.exercises.length :
96
+ 0
97
+ } exercises found`
98
+ )
99
+
100
+ const neededPlugins = await checkNotInstalledPlugins(
101
+ configObject?.exercises || [],
102
+ installedPlugins,
103
+ this
104
+ )
105
+
106
+ const allDepsInstalled = await checkNotInstalledDependencies(
107
+ neededPlugins.needed
108
+ )
109
+ if (!allDepsInstalled) {
110
+ this.exit(1)
111
+ }
112
+
113
+ const appAlreadyExists = checkIfDirectoryExists(
114
+ `${config?.dirPath}/_app`
115
+ )
116
+
117
+ if (!appAlreadyExists) {
118
+ // download app and decompress
119
+ await downloadEditor(
120
+ config?.editor.version,
121
+ `${config?.dirPath}/app.tar.gz`
122
+ )
123
+
124
+ Console.info("Decompressing LearnPack UI, this may take a minute...")
125
+ await decompress(
126
+ `${config?.dirPath}/app.tar.gz`,
127
+ `${config?.dirPath}/_app/`
128
+ )
129
+ }
130
+
131
+ // listen to socket commands
132
+ if (config && this.configManager) {
133
+ const server = await createServer(
134
+ configObject,
135
+ this.configManager,
136
+ process.env.NODE_ENV === "test"
137
+ )
138
+ server.setMaxListeners(25)
139
+
140
+ // I should call a method to get the EventListener
141
+ const dispatcher = queue.dispatcher({
142
+ create: true,
143
+ path: `${config.dirPath}/vscode_queue.json`,
144
+ })
145
+
146
+ if (configObject.exercises) {
147
+ const agent = configObject.config?.editor.agent || ""
148
+ const path = configObject.config?.dirPath || ""
149
+ const tutorialSlug = configObject.config?.slug || ""
150
+
151
+ const steps = configObject.exercises.map(
152
+ (e: IExercise, index): TStep => ({
153
+ slug: e.slug,
154
+ position: e.position || index,
155
+ files: e.files,
156
+ ai_interactions: [],
157
+ compilations: [],
158
+ tests: [],
159
+ is_testeable: e.graded || false,
160
+ })
161
+ )
162
+ if (path && steps.length > 0) {
163
+ TelemetryManager.start(agent, steps, path, tutorialSlug)
164
+ }
165
+
166
+ if (config.telemetry) {
167
+ TelemetryManager.urls = config.telemetry
168
+ }
169
+ }
170
+
171
+ socket.start(config, server, false)
172
+
173
+ socket.on("open", (data: IGitpodData) => {
174
+ Console.debug("Opening these files: ", data)
175
+
176
+ const files = prioritizeHTMLFile(data.files)
177
+
178
+ if (config.editor.agent === "os") {
179
+ eventManager.enqueue(dispatcher.events.OPEN_FILES, files)
180
+ } else {
181
+ dispatcher.enqueue(dispatcher.events.OPEN_FILES, files)
182
+ }
183
+
184
+ socket.ready("Ready to compile...")
185
+ })
186
+
187
+ socket.on("open_window", (data: TOpenWindowData) => {
188
+ Console.debug("Opening window: ", data)
189
+ // cli.open(data.url); This uses XDG under the ground
190
+ if (config.os !== "linux" || (config.os === "linux" && hasXDG)) {
191
+ eventManager.enqueue(dispatcher.events.OPEN_WINDOW, data)
192
+ } else {
193
+ dispatcher.enqueue(dispatcher.events.OPEN_WINDOW, data)
194
+ }
195
+
196
+ socket.log("open_window", "", undefined, data.url)
197
+ })
198
+
199
+ socket.on("open_terminal", (exercise: IExerciseData) => {
200
+ Console.debug("Opening terminal: ", exercise)
201
+ // eventManager.enqueue(dispatcher.events.OPEN_TERMINAL, exercise);
202
+ if (config.editor.agent === "vscode") {
203
+ dispatcher.enqueue(dispatcher.events.OPEN_TERMINAL, exercise)
204
+ }
205
+ })
206
+
207
+ socket.on("reset", (exercise: IExerciseData) => {
208
+ try {
209
+ this.configManager?.reset(exercise.exerciseSlug)
210
+ socket.ready("Ready to compile...")
211
+ } catch (error) {
212
+ socket.error(
213
+ "compiler-error",
214
+ (error as TypeError).message ||
215
+ "There was an error reseting the exercise"
216
+ )
217
+ setTimeout(() => socket.ready("Ready to compile..."), 2000)
218
+ }
219
+ })
220
+
221
+ socket.on("build", async (data: IExerciseData) => {
222
+ const exercise = this.configManager?.getExercise(data.exerciseSlug)
223
+
224
+ if (!exercise?.language) {
225
+ socket.error(
226
+ "compiler-error",
227
+ "Impossible to detect language to build for " +
228
+ data.exerciseSlug +
229
+ "..."
230
+ )
231
+ return
232
+ }
233
+
234
+ socket.log(
235
+ "compiling",
236
+ "Building exercise " +
237
+ data.exerciseSlug +
238
+ " with " +
239
+ exercise.language +
240
+ "..."
241
+ )
242
+ await this.config.runHook("action", {
243
+ action: "compile",
244
+ socket,
245
+ configuration: config,
246
+ exercise,
247
+ telemetry: TelemetryManager,
248
+ })
249
+ })
250
+
251
+ socket.on("ai_interaction", (data: any) => {
252
+ const { stepPosition, event, eventData } = data
253
+ TelemetryManager.registerStepEvent(stepPosition, event, eventData)
254
+ })
255
+
256
+ socket.on("test", async (data: IExerciseData) => {
257
+ const exercise = this.configManager?.getExercise(data.exerciseSlug)
258
+
259
+ if (!exercise?.language) {
260
+ socket.error(
261
+ "compiler-error",
262
+ "Impossible to detect engine language for testing for " +
263
+ data.exerciseSlug +
264
+ "..."
265
+ )
266
+ return
267
+ }
268
+
269
+ if (
270
+ config?.disabledActions!.includes("test") ||
271
+ config?.disableGrading
272
+ ) {
273
+ socket.ready("Grading is disabled on configuration")
274
+ return true
275
+ }
276
+
277
+ socket.log(
278
+ "testing",
279
+ "Testing your exercise using the " + exercise.language + " engine."
280
+ )
281
+
282
+ await this.config.runHook("action", {
283
+ action: "test",
284
+ socket,
285
+ configuration: config,
286
+ exercise,
287
+ telemetry: TelemetryManager,
288
+ })
289
+
290
+ this.configManager?.save()
291
+
292
+ return true
293
+ })
294
+
295
+ const terminate = async () => {
296
+ Console.error("Terminating Learnpack...")
297
+ await TelemetryManager.submit()
298
+ this.configManager?.noCurrentExercise()
299
+ dispatcher.enqueue(dispatcher.events.END)
300
+ process.exit()
301
+ }
302
+
303
+ server.on("close", terminate)
304
+ process.on("SIGINT", terminate)
305
+ process.on("SIGTERM", terminate)
306
+ process.on("SIGHUP", terminate)
307
+
308
+ // finish the server startup
309
+ setTimeout(() => dispatcher.enqueue(dispatcher.events.RUNNING), 1000)
310
+
311
+ // start watching for file changes
312
+ if (StartCommand.flags.watch)
313
+ this.configManager.watchIndex(_filename => {
314
+ // Instead of reloading with socket.reload(), I just notify the frontend for the file change
315
+ socket.emit("file_change", "ready", _filename)
316
+ })
317
+ }
318
+ }
319
+ }
320
+ }