@learnpack/learnpack 2.1.44 → 2.1.46

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,42 +1,253 @@
1
- import { IExercise } from "../models/exercise-obj"
2
- export const checkNotInstalledPlugins = (
3
- exercises: IExercise[],
4
- installedPlugins: string[]
5
- ) => {
6
- const usefulExtensions = new Set(["py", "js", "jsx", "html"])
7
- const foundExtensions: string[] = []
8
-
9
- for (const e of exercises) {
10
- for (const f of e.files) {
11
- const ext = f.name.split(".").pop()
12
- if (ext && usefulExtensions.has(ext) && !foundExtensions.includes(ext)) {
13
- foundExtensions.push(ext)
14
- }
15
- }
16
- }
17
-
18
- const neededPlugins: string[] = []
19
- if (foundExtensions.length > 0) {
20
- if (foundExtensions.includes("html") && foundExtensions.includes("js")) {
21
- neededPlugins.push("@learnpack/dom")
22
- }
23
-
24
- if (foundExtensions.includes("html") && !foundExtensions.includes("js")) {
25
- neededPlugins.push("@learnpack/html")
26
- }
27
-
28
- if (foundExtensions.includes("jsx")) {
29
- neededPlugins.push("@learnpack/react")
30
- }
31
-
32
- if (foundExtensions.includes("py")) {
33
- neededPlugins.push("@learnpack/python")
34
- }
35
-
36
- if (foundExtensions.includes("js")) {
37
- neededPlugins.push("@learnpack/node")
38
- }
39
- }
40
-
41
- return neededPlugins.filter(item => !installedPlugins.includes(item))
42
- }
1
+ import { IExercise } from "../models/exercise-obj"
2
+ import { promisify } from "util"
3
+ import { exec as execCallback } from "child_process"
4
+ const exec = promisify(execCallback)
5
+
6
+ import { cli } from "cli-ux"
7
+ import Console from "./console"
8
+ import { TGrading } from "../models/config"
9
+
10
+ type TNeededPlugins = {
11
+ needed: string[];
12
+ notInstalled: string[];
13
+ };
14
+ type TPackageManager = "npm" | "pip";
15
+
16
+ export const checkNotInstalledPlugins = async (
17
+ exercises: IExercise[],
18
+ installedPlugins: string[],
19
+ command: any
20
+ ): Promise<TNeededPlugins> => {
21
+ Console.info("Checking needed plugins...")
22
+ const usefulExtensions = new Set(["py", "js", "jsx", "html"])
23
+ const foundExtensions: string[] = []
24
+ const testingExtensions = []
25
+ const neededPlugins: string[] = []
26
+ let someExerciseWithOnlyTestFile = false // I will suppose that there are not exercises with only readme and test files
27
+
28
+ // See the extensions of the files that are not tests files
29
+ for (const e of exercises) {
30
+ const notReadmeFiles = e.files.filter(
31
+ f => !f.name.toLowerCase().includes("readme")
32
+ )
33
+
34
+ if (!someExerciseWithOnlyTestFile) {
35
+ someExerciseWithOnlyTestFile = notReadmeFiles.every(f =>
36
+ f.name.includes("test")
37
+ )
38
+ }
39
+
40
+ for (const f of notReadmeFiles) {
41
+ // There are some courses with only test files in js and grading: incremental
42
+ // TODO: We should know in the incremental exercises which compilers are needed
43
+
44
+ const ext = f.name.split(".").pop()
45
+ if (f.name.includes("test")) {
46
+ testingExtensions.push(ext)
47
+ continue
48
+ }
49
+
50
+ if (ext && usefulExtensions.has(ext) && !foundExtensions.includes(ext)) {
51
+ foundExtensions.push(ext)
52
+ }
53
+ }
54
+ }
55
+
56
+ if (foundExtensions.length === 0)
57
+ return {
58
+ needed: [],
59
+ notInstalled: [],
60
+ }
61
+
62
+ if (foundExtensions.includes("html") && foundExtensions.includes("js")) {
63
+ neededPlugins.push("@learnpack/dom")
64
+ }
65
+
66
+ if (foundExtensions.includes("html") && !foundExtensions.includes("js")) {
67
+ neededPlugins.push("@learnpack/html")
68
+ }
69
+
70
+ if (foundExtensions.includes("jsx")) {
71
+ neededPlugins.push("@learnpack/react")
72
+ }
73
+
74
+ if (foundExtensions.includes("py") || testingExtensions.includes("py")) {
75
+ neededPlugins.push("@learnpack/python")
76
+ }
77
+
78
+ if (
79
+ (foundExtensions.includes("js") && !foundExtensions.includes("html")) ||
80
+ (testingExtensions.includes("js") && someExerciseWithOnlyTestFile)
81
+ ) {
82
+ neededPlugins.push("@learnpack/node")
83
+ }
84
+
85
+ const notInstalled = neededPlugins.filter(
86
+ item => !installedPlugins.includes(item)
87
+ )
88
+
89
+ if (notInstalled.length > 0) {
90
+ Console.error(
91
+ "These plugins are not installed but required: ",
92
+ notInstalled
93
+ )
94
+ const confirmInstall = await cli.confirm(
95
+ "Do you want to install the needed plugins? (y/n)"
96
+ )
97
+ if (confirmInstall) {
98
+ Console.info("Installing the needed plugins...")
99
+ for await (const p of notInstalled) {
100
+ await command.config.runCommand(`plugins:install`, [p])
101
+ Console.log(`Plugin ${p} installed`)
102
+ }
103
+
104
+ Console.log(
105
+ "All needed plugins installed, please restart LearnPack to load them."
106
+ )
107
+ Console.info("Run: $ learnpack start")
108
+ command.exit(0)
109
+ } else {
110
+ Console.error(
111
+ "You need to install the plugins to complete this exercise"
112
+ )
113
+ Console.info(
114
+ "To install the plugins run each of the following commands: "
115
+ )
116
+ for (const p of notInstalled) {
117
+ Console.log(`learnpack plugins:install ${p}`)
118
+ }
119
+
120
+ command.exit(1)
121
+ }
122
+ }
123
+
124
+ return {
125
+ needed: neededPlugins,
126
+ notInstalled,
127
+ }
128
+ }
129
+
130
+ function includesAll(baseString: string, elementsArray: string[]) {
131
+ return elementsArray.every(element => baseString.includes(element))
132
+ }
133
+
134
+ const installDependencies = async (
135
+ deps: string[],
136
+ packageManager: TPackageManager
137
+ ) => {
138
+ let command = ""
139
+ if (packageManager === "npm") {
140
+ command = `npm install -g ${deps.join(" ")}`
141
+ } else if (packageManager === "pip") {
142
+ command = `pip install ${deps.join(" ")}`
143
+ }
144
+
145
+ const { stdout, stderr } = await exec(command)
146
+ if (stderr && (stderr.includes("npm ERR!") || stderr.includes("Traceback"))) {
147
+ Console.error(`Error executing ${command}.`)
148
+ Console.error(stderr)
149
+ return
150
+ }
151
+
152
+ Console.info(`Dependencies ${deps.join(" ")} installed...`)
153
+ return true
154
+ }
155
+
156
+ export const checkNotInstalledDependencies = async (
157
+ neededPlugins: string[]
158
+ ) => {
159
+ Console.info("Checking needed dependencies...")
160
+ const jsPluginsDependencies = [
161
+ "jest@29.7.0",
162
+ "jest-environment-jsdom@29.7.0",
163
+ ]
164
+ const pyPluginsDependencies = ["pytest==6.2.5", "pytest-testdox", "mock"]
165
+
166
+ const npmLsCommand = "npm ls jest jest-environment-jsdom -g"
167
+
168
+ let pytestNeeded = false
169
+ let jestNeeded = false
170
+
171
+ if (
172
+ neededPlugins.includes("@learnpack/dom") ||
173
+ neededPlugins.includes("@learnpack/html") ||
174
+ neededPlugins.includes("@learnpack/react") ||
175
+ neededPlugins.includes("@learnpack/node")
176
+ ) {
177
+ jestNeeded = true
178
+ }
179
+
180
+ if ("@learnpack/python" in neededPlugins) {
181
+ pytestNeeded = true
182
+ }
183
+
184
+ if (jestNeeded) {
185
+ const { stdout, stderr } = await exec(
186
+ "npm ls jest jest-environment-jsdom -g"
187
+ )
188
+ if (stderr) {
189
+ Console.error(`Error executing ${npmLsCommand}. Use debug for more info`)
190
+ Console.debug(stderr)
191
+ return false
192
+ }
193
+
194
+ if (includesAll(stdout, jsPluginsDependencies))
195
+ return true
196
+
197
+ Console.error("The jest dependencies are not installed")
198
+ const confirmInstall = await cli.confirm(
199
+ "Do you want to install the needed dependencies? (y/n)"
200
+ )
201
+ if (!confirmInstall) {
202
+ Console.error(
203
+ `The exercises can't be tested without the following dependencies: ${jsPluginsDependencies.join(
204
+ ", "
205
+ )}`
206
+ )
207
+ Console.info(
208
+ `Please install them and try again. Run the following command to install them: \nnpm install -g ${jsPluginsDependencies.join(
209
+ " "
210
+ )}`
211
+ )
212
+ return false
213
+ }
214
+
215
+ Console.log("Installing jest dependencies...")
216
+ await installDependencies(jsPluginsDependencies, "npm")
217
+ }
218
+
219
+ if (pytestNeeded) {
220
+ const { stdout, stderr } = await exec("pip list")
221
+ if (stderr) {
222
+ Console.error(`Error executing pip list. Use debug for more info`)
223
+ Console.debug(stderr)
224
+ return
225
+ }
226
+
227
+ if (includesAll(stdout, pyPluginsDependencies))
228
+ return true
229
+
230
+ Console.error("The pytest dependencies are not installed")
231
+ const confirmInstall = await cli.confirm(
232
+ "Do you want to install the needed dependencies? (y/n)"
233
+ )
234
+ if (!confirmInstall) {
235
+ Console.error(
236
+ `The exercises can't be tested without the following dependencies: ${pyPluginsDependencies.join(
237
+ ", "
238
+ )}`
239
+ )
240
+ Console.info(
241
+ `Please install them and try again. Run the following command to install them: \npip install ${pyPluginsDependencies.join(
242
+ " "
243
+ )}`
244
+ )
245
+ return false
246
+ }
247
+
248
+ Console.log("Installing pytest dependencies...")
249
+ await installDependencies(pyPluginsDependencies, "pip")
250
+ }
251
+
252
+ return true
253
+ }
@@ -1,24 +1,24 @@
1
- import * as chalk from 'chalk'
2
-
3
- export default {
4
- // _debug: true,
5
- _debug: process.env.DEBUG === 'true',
6
- startDebug: function () {
7
- this._debug = true
8
- },
9
- log: (msg: string | Array<string>, ...args: Array<any>) =>
10
- console.log(chalk.gray(msg), ...args),
11
- error: (msg: string, ...args: Array<any>) =>
12
- console.log(chalk.red('' + msg), ...args),
13
- success: (msg: string, ...args: Array<any>) =>
14
- console.log(chalk.green('' + msg), ...args),
15
- info: (msg: string, ...args: Array<any>) =>
16
- console.log(chalk.blue('' + msg), ...args),
17
- help: (msg: string) =>
18
- console.log(`${chalk.white.bold('⚠ help:')} ${chalk.white(msg)}`),
19
- debug(...args: Array<any>) {
20
- this._debug && console.log(chalk.magentaBright('⚠ debug: '), args)
21
- },
22
- warning: (msg: string) =>
23
- console.log(`${chalk.yellow('⚠ warning:')} ${chalk.yellow(msg)}`),
24
- }
1
+ import * as chalk from "chalk"
2
+
3
+ export default {
4
+ // _debug: true,
5
+ _debug: process.env.DEBUG === "true",
6
+ startDebug: function () {
7
+ this._debug = true
8
+ },
9
+ log: (msg: string | Array<string>, ...args: Array<any>) =>
10
+ console.log(chalk.gray(msg), ...args),
11
+ error: (msg: string, ...args: Array<any>) =>
12
+ console.log(chalk.red("" + msg), ...args),
13
+ success: (msg: string, ...args: Array<any>) =>
14
+ console.log(chalk.green("" + msg), ...args),
15
+ info: (msg: any, ...args: Array<any>) =>
16
+ console.log(chalk.blue("" + msg), ...args),
17
+ help: (msg: string) =>
18
+ console.log(`${chalk.white.bold("⚠ help:")} ${chalk.white(msg)}`),
19
+ debug(...args: Array<any>) {
20
+ this._debug && console.log(chalk.magentaBright("⚠ debug: "), args)
21
+ },
22
+ warning: (msg: string) =>
23
+ console.log(`${chalk.yellow("⚠ warning:")} ${chalk.yellow(msg)}`),
24
+ }