@learnpack/learnpack 5.0.28 → 5.0.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -12
- package/lib/commands/breakToken.js +11 -3
- package/lib/commands/init.js +69 -38
- package/lib/commands/logout.js +6 -11
- package/lib/commands/publish.js +1 -1
- package/lib/commands/start.js +6 -12
- package/lib/managers/config/index.js +25 -0
- package/lib/managers/file.js +1 -1
- package/lib/managers/server/routes.js +31 -5
- package/lib/managers/session.js +3 -0
- package/lib/models/config-manager.d.ts +2 -0
- package/lib/utils/creatorUtilities.d.ts +7 -2
- package/lib/utils/creatorUtilities.js +88 -6
- package/lib/utils/rigoActions.d.ts +6 -0
- package/lib/utils/rigoActions.js +16 -0
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
- package/src/commands/breakToken.ts +13 -2
- package/src/commands/init.ts +103 -46
- package/src/commands/logout.ts +38 -43
- package/src/commands/publish.ts +1 -1
- package/src/commands/start.ts +7 -14
- package/src/managers/config/index.ts +26 -0
- package/src/managers/file.ts +1 -1
- package/src/managers/server/routes.ts +43 -3
- package/src/managers/session.ts +4 -0
- package/src/models/config-manager.ts +25 -23
- package/src/utils/creatorUtilities.ts +241 -147
- package/src/utils/rigoActions.ts +30 -0
- package/src/utils/templates/incremental/.github/workflows/learnpack-audit.yml +29 -0
- package/src/utils/templates/isolated/.github/workflows/learnpack-audit.yml +29 -0
@@ -469,6 +469,7 @@ fs.unlinkSync(_path)
|
|
469
469
|
}
|
470
470
|
}
|
471
471
|
},
|
472
|
+
|
472
473
|
buildIndex: function () {
|
473
474
|
Console.info("Building the exercise index...")
|
474
475
|
|
@@ -511,6 +512,31 @@ fs.mkdirSync(confPath.base)
|
|
511
512
|
[exercise(configObj?.config?.exercisesPath || "", 0, configObj)]
|
512
513
|
this.save()
|
513
514
|
},
|
515
|
+
createExercise: (slug: string, content: string, language: string) => {
|
516
|
+
try {
|
517
|
+
const dirPath = `${configObj.config?.exercisesPath}/${slug}`
|
518
|
+
if (!fs.existsSync(dirPath))
|
519
|
+
fs.mkdirSync(dirPath, { recursive: true })
|
520
|
+
const isEnglish = language === "us" || language === "en"
|
521
|
+
const fileName = isEnglish ? "README.md" : `README.${language}.md`
|
522
|
+
fs.writeFileSync(`${dirPath}/${fileName}`, content)
|
523
|
+
|
524
|
+
return true
|
525
|
+
} catch (error) {
|
526
|
+
Console.error("Error creating exercise: ", error)
|
527
|
+
return false
|
528
|
+
}
|
529
|
+
},
|
530
|
+
|
531
|
+
deleteExercise: (slug: string) => {
|
532
|
+
const dirPath = `${configObj.config?.exercisesPath}/${slug}`
|
533
|
+
if (fs.existsSync(dirPath)) {
|
534
|
+
fs.rmSync(dirPath, { recursive: true, force: true })
|
535
|
+
return true
|
536
|
+
}
|
537
|
+
|
538
|
+
return false
|
539
|
+
},
|
514
540
|
watchIndex: function (onChange: (filename: string) => void) {
|
515
541
|
if (configObj.config && !configObj.config.exercisesPath)
|
516
542
|
throw ValidationError(
|
package/src/managers/file.ts
CHANGED
@@ -123,7 +123,7 @@ export const download = (url: string, dest: string) => {
|
|
123
123
|
})
|
124
124
|
file.on("error", err => {
|
125
125
|
file.close()
|
126
|
-
if (err.
|
126
|
+
if (err.name === "EEXIST") {
|
127
127
|
Console.debug("File already exists")
|
128
128
|
resolve("File already exists")
|
129
129
|
} else {
|
@@ -279,9 +279,7 @@ throw new Error("File not found: " + filePath)
|
|
279
279
|
TelemetryManager.registerStepEvent(exercise.position, "open_step", {})
|
280
280
|
}
|
281
281
|
|
282
|
-
if (configObject.config?.editor.agent
|
283
|
-
eventManager.enqueue(dispatcher.events.START_EXERCISE, exercise)
|
284
|
-
} else {
|
282
|
+
if (configObject.config?.editor.agent !== "os") {
|
285
283
|
dispatcher.enqueue(dispatcher.events.START_EXERCISE, req.params.slug)
|
286
284
|
}
|
287
285
|
|
@@ -383,6 +381,48 @@ throw new Error("File not found: " + filePath)
|
|
383
381
|
})
|
384
382
|
)
|
385
383
|
|
384
|
+
app.delete(
|
385
|
+
"/exercise/:slug/delete",
|
386
|
+
withHandler(async (req: express.Request, res: express.Response) => {
|
387
|
+
const exerciseDeleted = configManager.deleteExercise(req.params.slug)
|
388
|
+
if (exerciseDeleted) {
|
389
|
+
configManager.buildIndex()
|
390
|
+
res.json({ status: "ok" })
|
391
|
+
} else {
|
392
|
+
res.status(500).json({ error: "Failed to delete exercise" })
|
393
|
+
}
|
394
|
+
})
|
395
|
+
)
|
396
|
+
|
397
|
+
app.post(
|
398
|
+
"/exercise/:slug/create",
|
399
|
+
jsonBodyParser,
|
400
|
+
withHandler(async (req: express.Request, res: express.Response) => {
|
401
|
+
const { title, readme, language } = req.body
|
402
|
+
const { slug } = req.params
|
403
|
+
|
404
|
+
if (!title || !readme || !language) {
|
405
|
+
return res.status(400).json({ error: "Missing required fields" })
|
406
|
+
}
|
407
|
+
|
408
|
+
try {
|
409
|
+
const exerciseCreated = await configManager.createExercise(
|
410
|
+
slug,
|
411
|
+
readme,
|
412
|
+
language
|
413
|
+
)
|
414
|
+
if (exerciseCreated) {
|
415
|
+
configManager.buildIndex()
|
416
|
+
res.json({ status: "ok" })
|
417
|
+
} else {
|
418
|
+
res.status(500).json({ error: "Failed to create exercise" })
|
419
|
+
}
|
420
|
+
} catch {
|
421
|
+
res.status(500).json({ error: "Failed to create exercise" })
|
422
|
+
}
|
423
|
+
})
|
424
|
+
)
|
425
|
+
|
386
426
|
const textBodyParser = bodyParser.text()
|
387
427
|
app.put(
|
388
428
|
"/exercise/:slug/file/:fileName",
|
package/src/managers/session.ts
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
-
import { IConfigObj, TGrading } from "./config"
|
2
|
-
import { IExercise } from "./exercise-obj"
|
3
|
-
|
4
|
-
export interface IConfigManagerAttributes {
|
5
|
-
grading: TGrading
|
6
|
-
disableGrading: boolean
|
7
|
-
version: string
|
8
|
-
mode?: string
|
9
|
-
}
|
10
|
-
|
11
|
-
export interface IConfigManager {
|
12
|
-
validLanguages?: any
|
13
|
-
get: () => IConfigObj
|
14
|
-
clean: () => void
|
15
|
-
getExercise: (slug: string | undefined) => IExercise
|
16
|
-
startExercise: (slug: string) => IExercise
|
17
|
-
reset: (slug: string) => void
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
1
|
+
import { IConfigObj, TGrading } from "./config"
|
2
|
+
import { IExercise } from "./exercise-obj"
|
3
|
+
|
4
|
+
export interface IConfigManagerAttributes {
|
5
|
+
grading: TGrading
|
6
|
+
disableGrading: boolean
|
7
|
+
version: string
|
8
|
+
mode?: string
|
9
|
+
}
|
10
|
+
|
11
|
+
export interface IConfigManager {
|
12
|
+
validLanguages?: any
|
13
|
+
get: () => IConfigObj
|
14
|
+
clean: () => void
|
15
|
+
getExercise: (slug: string | undefined) => IExercise
|
16
|
+
startExercise: (slug: string) => IExercise
|
17
|
+
reset: (slug: string) => void
|
18
|
+
createExercise: (slug: string, content: string, language: string) => boolean
|
19
|
+
deleteExercise: (slug: string) => boolean
|
20
|
+
buildIndex: () => boolean | void
|
21
|
+
watchIndex: (onChange: (...args: Array<any>) => void) => void
|
22
|
+
save: () => void
|
23
|
+
noCurrentExercise: () => void
|
24
|
+
getAllExercises: () => IExercise[]
|
25
|
+
}
|
@@ -1,147 +1,241 @@
|
|
1
|
-
// eslint-disable-next-line
|
2
|
-
const frontMatter = require("front-matter")
|
3
|
-
import * as path from "path"
|
4
|
-
|
5
|
-
import
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
}
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
}
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
.
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
return
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
}
|
146
|
-
|
147
|
-
|
1
|
+
// eslint-disable-next-line
|
2
|
+
const frontMatter = require("front-matter")
|
3
|
+
import * as path from "path"
|
4
|
+
import * as fs from "fs"
|
5
|
+
import { homedir } from "os"
|
6
|
+
import { join } from "path"
|
7
|
+
import { exec } from "child_process"
|
8
|
+
import { promisify } from "util"
|
9
|
+
|
10
|
+
import * as yaml from "js-yaml"
|
11
|
+
|
12
|
+
type TEstimateReadingTimeReturns = {
|
13
|
+
minutes: number
|
14
|
+
words: number
|
15
|
+
}
|
16
|
+
|
17
|
+
export const estimateReadingTime = (
|
18
|
+
text: string,
|
19
|
+
wordsPerMinute = 150
|
20
|
+
): TEstimateReadingTimeReturns => {
|
21
|
+
const words = text.trim().split(/\s+/).length
|
22
|
+
const minutes = words / wordsPerMinute
|
23
|
+
|
24
|
+
if (minutes < 1) {
|
25
|
+
if (words === 0)
|
26
|
+
return {
|
27
|
+
minutes: 1,
|
28
|
+
words,
|
29
|
+
}
|
30
|
+
} else {
|
31
|
+
return {
|
32
|
+
minutes,
|
33
|
+
words,
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
return {
|
38
|
+
minutes: 1,
|
39
|
+
words,
|
40
|
+
}
|
41
|
+
}
|
42
|
+
|
43
|
+
export type PackageInfo = {
|
44
|
+
grading: string
|
45
|
+
difficulty: string
|
46
|
+
duration: number
|
47
|
+
description: {
|
48
|
+
us: string
|
49
|
+
}
|
50
|
+
title: {
|
51
|
+
us: string
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
export function checkReadingTime(
|
56
|
+
markdown: string,
|
57
|
+
wordsPerMinute = 200,
|
58
|
+
maxMinutes = 1
|
59
|
+
): {
|
60
|
+
newMarkdown: string
|
61
|
+
exceedsThreshold: boolean
|
62
|
+
minutes: number
|
63
|
+
body: string
|
64
|
+
} {
|
65
|
+
const parsed = frontMatter(markdown)
|
66
|
+
|
67
|
+
const readingTime = estimateReadingTime(parsed.body, wordsPerMinute)
|
68
|
+
|
69
|
+
let attributes = parsed.attributes ? parsed.attributes : {}
|
70
|
+
|
71
|
+
if (typeof parsed.attributes !== "object") {
|
72
|
+
attributes = {}
|
73
|
+
}
|
74
|
+
|
75
|
+
const updatedAttributes = {
|
76
|
+
...attributes,
|
77
|
+
readingTime,
|
78
|
+
}
|
79
|
+
|
80
|
+
let yamlFrontMatter = ""
|
81
|
+
try {
|
82
|
+
yamlFrontMatter = yaml.dump(updatedAttributes).trim()
|
83
|
+
} catch {
|
84
|
+
return {
|
85
|
+
newMarkdown: "",
|
86
|
+
exceedsThreshold: false,
|
87
|
+
minutes: 0,
|
88
|
+
body: "",
|
89
|
+
}
|
90
|
+
}
|
91
|
+
|
92
|
+
// Reconstruct the markdown with the front matter
|
93
|
+
const newMarkdown = `---\n${yamlFrontMatter}\n---\n\n${parsed.body}`
|
94
|
+
|
95
|
+
return {
|
96
|
+
newMarkdown,
|
97
|
+
exceedsThreshold: readingTime.minutes > maxMinutes,
|
98
|
+
minutes: readingTime.minutes,
|
99
|
+
body: parsed.body,
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
const slugify = (text: string) => {
|
104
|
+
return text
|
105
|
+
.toString()
|
106
|
+
.normalize("NFD")
|
107
|
+
.replace(/[\u0300-\u036F]/g, "")
|
108
|
+
.toLowerCase()
|
109
|
+
.trim()
|
110
|
+
.replace(/\s+/g, "-")
|
111
|
+
.replace(/[^\w-]+/g, "")
|
112
|
+
}
|
113
|
+
|
114
|
+
export const getExInfo = (title: string) => {
|
115
|
+
// Example title: '1.0 - Introduction to AI [READ: Small introduction to important concepts such as AI, machine learning, and their applications]'
|
116
|
+
let [exNumber, exTitle] = title.split(" - ")
|
117
|
+
|
118
|
+
// Extract kind and description
|
119
|
+
const kindMatch = exTitle.match(/\[(.*?):(.*?)]/)
|
120
|
+
const kind = kindMatch ? kindMatch[1].trim().toLowerCase() : "read"
|
121
|
+
const description = kindMatch ? kindMatch[2].trim() : ""
|
122
|
+
|
123
|
+
exNumber = exNumber.trim()
|
124
|
+
// Clean title
|
125
|
+
exTitle = exTitle.replace(kindMatch?.[0] || "", "").trim()
|
126
|
+
exTitle = slugify(exTitle)
|
127
|
+
|
128
|
+
return {
|
129
|
+
exNumber,
|
130
|
+
kind,
|
131
|
+
description,
|
132
|
+
exTitle,
|
133
|
+
}
|
134
|
+
}
|
135
|
+
|
136
|
+
export function extractImagesFromMarkdown(markdown: string) {
|
137
|
+
const imageRegex = /!\[([^\]]*)]\(([^)]+)\)/g
|
138
|
+
const images = []
|
139
|
+
let match
|
140
|
+
|
141
|
+
while ((match = imageRegex.exec(markdown)) !== null) {
|
142
|
+
const altText = match[1]
|
143
|
+
const url = match[2]
|
144
|
+
images.push({ alt: altText, url: url })
|
145
|
+
}
|
146
|
+
|
147
|
+
return images
|
148
|
+
}
|
149
|
+
|
150
|
+
export function getFilenameFromUrl(url: string) {
|
151
|
+
return path.basename(url)
|
152
|
+
}
|
153
|
+
|
154
|
+
export const makePackageInfo = (choices: any) => {
|
155
|
+
const packageInfo = {
|
156
|
+
grading: choices.grading,
|
157
|
+
difficulty: "beginner",
|
158
|
+
duration: 5,
|
159
|
+
description: {
|
160
|
+
us: choices.description,
|
161
|
+
},
|
162
|
+
title: {
|
163
|
+
us: choices.title,
|
164
|
+
},
|
165
|
+
slug: choices.title
|
166
|
+
.toLowerCase()
|
167
|
+
.replace(/ /g, "-")
|
168
|
+
.replace(/[^\w-]+/g, ""),
|
169
|
+
}
|
170
|
+
return packageInfo
|
171
|
+
}
|
172
|
+
|
173
|
+
export function estimateDuration(listOfSteps: string[]): number {
|
174
|
+
let duration = 0
|
175
|
+
|
176
|
+
for (const step of listOfSteps) {
|
177
|
+
if (step.includes("[READ:")) {
|
178
|
+
duration += 1
|
179
|
+
} else if (step.includes("[QUIZ:")) {
|
180
|
+
duration += 2
|
181
|
+
} else if (step.includes("[CODE:")) {
|
182
|
+
duration += 3
|
183
|
+
}
|
184
|
+
}
|
185
|
+
|
186
|
+
return duration
|
187
|
+
}
|
188
|
+
|
189
|
+
const writeFilePromise = promisify(fs.writeFile)
|
190
|
+
const execPromise = promisify(exec)
|
191
|
+
|
192
|
+
const example_content = `
|
193
|
+
00.1 - Introduction to AI [READ: Small introduction to important concepts such as AI, machine learning, and their applications]
|
194
|
+
01.1 - Introduction to Machine Learning [READ: Small introduction to important concepts such as AI, machine learning, and their applications]
|
195
|
+
01.2 - Introduction to Deep Learning [QUIZ: Small introduction to important concepts such as AI, machine learning, and their applications]
|
196
|
+
02.1 - Test your knowledge [CODE: Code problem to solve]
|
197
|
+
`
|
198
|
+
|
199
|
+
export async function createFileOnDesktop() {
|
200
|
+
try {
|
201
|
+
const desktopPath = join(homedir(), "Desktop")
|
202
|
+
const filePath = join(desktopPath, "content_index.txt")
|
203
|
+
|
204
|
+
const content = example_content.trim()
|
205
|
+
|
206
|
+
await writeFilePromise(filePath, content)
|
207
|
+
console.log(`File created successfully at: ${filePath}`)
|
208
|
+
|
209
|
+
await openFile(filePath)
|
210
|
+
} catch (error) {
|
211
|
+
console.error("Error:", error)
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
async function openFile(filePath: string) {
|
216
|
+
const platform = process.platform
|
217
|
+
let command
|
218
|
+
|
219
|
+
if (platform === "win32") {
|
220
|
+
command = `start "" "${filePath}"`
|
221
|
+
} else if (platform === "darwin") {
|
222
|
+
command = `open "${filePath}"`
|
223
|
+
} else {
|
224
|
+
command = `xdg-open "${filePath}"`
|
225
|
+
}
|
226
|
+
|
227
|
+
try {
|
228
|
+
await execPromise(command)
|
229
|
+
console.log("File opened successfully.")
|
230
|
+
} catch (error) {
|
231
|
+
console.error("Error opening the file:", error)
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
export function getContentIndex() {
|
236
|
+
const desktopPath = join(homedir(), "Desktop")
|
237
|
+
const filePath = join(desktopPath, "content_index.txt")
|
238
|
+
|
239
|
+
const content = fs.readFileSync(filePath, "utf8")
|
240
|
+
return content
|
241
|
+
}
|
package/src/utils/rigoActions.ts
CHANGED
@@ -293,3 +293,33 @@ export async function createPreviewReadme(
|
|
293
293
|
})
|
294
294
|
fs.writeFileSync(path.join(tutorialDir, readmeFilename), readmeContent.answer)
|
295
295
|
}
|
296
|
+
|
297
|
+
// {"lesson": "The text of the lesson", "number_of_words": "Words lenght of the lesson", "expected_number_words": "The expected number of words"}
|
298
|
+
|
299
|
+
type TReduceReadmeInputs = {
|
300
|
+
lesson: string
|
301
|
+
number_of_words: string
|
302
|
+
expected_number_words: string
|
303
|
+
}
|
304
|
+
export async function reduceReadme(
|
305
|
+
rigoToken: string,
|
306
|
+
inputs: TReduceReadmeInputs
|
307
|
+
) {
|
308
|
+
try {
|
309
|
+
const response = await axios.post(
|
310
|
+
`${RIGOBOT_HOST}/v1/prompting/completion/588/`,
|
311
|
+
{ inputs, include_purpose_objective: false, execute_async: false },
|
312
|
+
{
|
313
|
+
headers: {
|
314
|
+
"Content-Type": "application/json",
|
315
|
+
Authorization: "Token " + rigoToken,
|
316
|
+
},
|
317
|
+
}
|
318
|
+
)
|
319
|
+
|
320
|
+
return response.data
|
321
|
+
} catch (error) {
|
322
|
+
Console.debug(error)
|
323
|
+
return null
|
324
|
+
}
|
325
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
3
|
+
|
4
|
+
name: Learnpack audit
|
5
|
+
|
6
|
+
on:
|
7
|
+
push:
|
8
|
+
branches: [ main ]
|
9
|
+
pull_request:
|
10
|
+
branches: [ main ]
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
build:
|
14
|
+
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
|
17
|
+
strategy:
|
18
|
+
matrix:
|
19
|
+
node-version: [20.x]
|
20
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
21
|
+
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v2
|
24
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
25
|
+
uses: actions/setup-node@v2
|
26
|
+
with:
|
27
|
+
node-version: ${{ matrix.node-version }}
|
28
|
+
- run: npm install @learnpack/learnpack@latest -g
|
29
|
+
- run: learnpack audit
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
2
|
+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
3
|
+
|
4
|
+
name: Learnpack audit
|
5
|
+
|
6
|
+
on:
|
7
|
+
push:
|
8
|
+
branches: [ main ]
|
9
|
+
pull_request:
|
10
|
+
branches: [ main ]
|
11
|
+
|
12
|
+
jobs:
|
13
|
+
build:
|
14
|
+
|
15
|
+
runs-on: ubuntu-latest
|
16
|
+
|
17
|
+
strategy:
|
18
|
+
matrix:
|
19
|
+
node-version: [20.x]
|
20
|
+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
21
|
+
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v2
|
24
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
25
|
+
uses: actions/setup-node@v2
|
26
|
+
with:
|
27
|
+
node-version: ${{ matrix.node-version }}
|
28
|
+
- run: npm install @learnpack/learnpack@latest -g
|
29
|
+
- run: learnpack audit
|