@learnpack/learnpack 5.0.37 → 5.0.38

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 CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @learnpack/learnpack
21
21
  $ learnpack COMMAND
22
22
  running command...
23
23
  $ learnpack (-v|--version|version)
24
- @learnpack/learnpack/5.0.37 win32-x64 node-v22.14.0
24
+ @learnpack/learnpack/5.0.38 win32-x64 node-v22.14.0
25
25
  $ learnpack --help [COMMAND]
26
26
  USAGE
27
27
  $ learnpack COMMAND
@@ -76,7 +76,7 @@ DESCRIPTION
76
76
  12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)
77
77
  ```
78
78
 
79
- _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\audit.ts)_
79
+ _See code: [src\commands\audit.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\audit.ts)_
80
80
 
81
81
  ## `learnpack breakToken`
82
82
 
@@ -91,7 +91,7 @@ OPTIONS
91
91
  -y, --yes Skip all prompts and initialize an empty project
92
92
  ```
93
93
 
94
- _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\breakToken.ts)_
94
+ _See code: [src\commands\breakToken.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\breakToken.ts)_
95
95
 
96
96
  ## `learnpack clean`
97
97
 
@@ -106,7 +106,7 @@ DESCRIPTION
106
106
  Extra documentation goes here
107
107
  ```
108
108
 
109
- _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\clean.ts)_
109
+ _See code: [src\commands\clean.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\clean.ts)_
110
110
 
111
111
  ## `learnpack download [PACKAGE]`
112
112
 
@@ -124,7 +124,7 @@ DESCRIPTION
124
124
  Extra documentation goes here
125
125
  ```
126
126
 
127
- _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\download.ts)_
127
+ _See code: [src\commands\download.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\download.ts)_
128
128
 
129
129
  ## `learnpack help [COMMAND]`
130
130
 
@@ -156,7 +156,7 @@ OPTIONS
156
156
  -y, --yes Skip all prompts and initialize an empty project
157
157
  ```
158
158
 
159
- _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\init.ts)_
159
+ _See code: [src\commands\init.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\init.ts)_
160
160
 
161
161
  ## `learnpack login [PACKAGE]`
162
162
 
@@ -174,7 +174,7 @@ DESCRIPTION
174
174
  Extra documentation goes here
175
175
  ```
176
176
 
177
- _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\login.ts)_
177
+ _See code: [src\commands\login.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\login.ts)_
178
178
 
179
179
  ## `learnpack logout [PACKAGE]`
180
180
 
@@ -192,7 +192,7 @@ DESCRIPTION
192
192
  Extra documentation goes here
193
193
  ```
194
194
 
195
- _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\logout.ts)_
195
+ _See code: [src\commands\logout.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\logout.ts)_
196
196
 
197
197
  ## `learnpack plugins`
198
198
 
@@ -323,7 +323,7 @@ OPTIONS
323
323
  -h, --help show CLI help
324
324
  ```
325
325
 
326
- _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\publish.ts)_
326
+ _See code: [src\commands\publish.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\publish.ts)_
327
327
 
328
328
  ## `learnpack start`
329
329
 
@@ -345,7 +345,7 @@ OPTIONS
345
345
  -y, --yes Skip all prompts and initialize an empty project
346
346
  ```
347
347
 
348
- _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\start.ts)_
348
+ _See code: [src\commands\start.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\start.ts)_
349
349
 
350
350
  ## `learnpack test [EXERCISESLUG]`
351
351
 
@@ -362,7 +362,7 @@ OPTIONS
362
362
  -y, --yes Skip all prompts and initialize an empty project
363
363
  ```
364
364
 
365
- _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\test.ts)_
365
+ _See code: [src\commands\test.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\test.ts)_
366
366
 
367
367
  ## `learnpack translate`
368
368
 
@@ -376,7 +376,7 @@ OPTIONS
376
376
  -y, --yes Skip all prompts and initialize an empty project
377
377
  ```
378
378
 
379
- _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.37/src\commands\translate.ts)_
379
+ _See code: [src\commands\translate.ts](https://github.com/learnpack/learnpack-cli/blob/v5.0.38/src\commands\translate.ts)_
380
380
  <!-- commandsstop -->
381
381
 
382
382
  > > > > > > > 0cb3e56d84c197f9d008836bb573eade212b7e57
@@ -15,6 +15,7 @@ const console_1 = require("../utils/console");
15
15
  const file_1 = require("../managers/file");
16
16
  const api_1 = require("../utils/api");
17
17
  const prompts = require("prompts");
18
+ const rigoActions_1 = require("../utils/rigoActions");
18
19
  const RIGOBOT_HOST = "https://rigobot.herokuapp.com";
19
20
  // const RIGOBOT_HOST =
20
21
  // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us116.gitpod.io"
@@ -70,10 +71,15 @@ class BuildCommand extends SessionCommand_1.default {
70
71
  // this.configManager?.clean()
71
72
  const configObject = (_a = this.configManager) === null || _a === void 0 ? void 0 : _a.get();
72
73
  let sessionPayload = await session_1.default.getPayload();
73
- if (!sessionPayload ||
74
- !sessionPayload.rigobot ||
75
- (sessionPayload.token && !(await api_1.default.validateToken(sessionPayload.token)))) {
76
- console_1.default.error("You must be logged in to upload a LearnPack package");
74
+ const sessionExists = sessionPayload && sessionPayload.rigobot;
75
+ const isValidToken = sessionExists && sessionPayload.rigobot.key ?
76
+ await (0, rigoActions_1.isValidRigoToken)(sessionPayload.rigobot.key) :
77
+ false;
78
+ const isValidBreathecodeToken = sessionExists && sessionPayload.token ?
79
+ await api_1.default.validateToken(sessionPayload.token) :
80
+ false;
81
+ if (!sessionExists || !isValidBreathecodeToken || !isValidToken) {
82
+ console_1.default.info("Almost there! First you need to login with 4Geeks.com to use AI Generation tool for creators. You can create a new account here: https://4geeks.com/creators");
77
83
  try {
78
84
  sessionPayload = await session_1.default.login();
79
85
  }
@@ -83,6 +89,11 @@ class BuildCommand extends SessionCommand_1.default {
83
89
  }
84
90
  }
85
91
  const rigoToken = sessionPayload.rigobot.key;
92
+ const consumable = await (0, api_1.getConsumable)(sessionPayload.token, "learnpack-publish");
93
+ if (consumable.count === 0) {
94
+ console_1.default.error("It seems you cannot publish tutorials. Make sure you creator subscription is up to date here: https://4geeks.com/profile/subscriptions. If you believe there is an issue you can always contact support@4geeks.com");
95
+ process.exit(1);
96
+ }
86
97
  if (configObject) {
87
98
  console_1.default.info("Cleaning configuration files");
88
99
  (_b = this.configManager) === null || _b === void 0 ? void 0 : _b.clean();
@@ -1 +1 @@
1
- {"version":"5.0.37","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
1
+ {"version":"5.0.38","commands":{"audit":{"id":"audit","description":"learnpack audit is the command in charge of creating an auditory of the repository\n...\nlearnpack audit checks for the following information in a repository:\n 1. The configuration object has slug, repository and description. (Error)\n 2. The command learnpack clean has been run. (Error)\n 3. If a markdown or test file doesn't have any content. (Error)\n 4. The links are accessing to valid servers. (Error)\n 5. The relative images are working (If they have the shortest path to the image or if the images exists in the assets). (Error)\n 6. The external images are working (If they are pointing to a valid server). (Error)\n 7. The exercises directory names are valid. (Error)\n 8. If an exercise doesn't have a README file. (Error)\n 9. The exercises array (Of the config file) has content. (Error)\n 10. The exercses have the same translations. (Warning)\n 11. The .gitignore file exists. (Warning)\n 12. If there is a file within the exercises folder but not inside of any particular exercise's folder. (Warning)\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"breakToken":{"id":"breakToken","description":"Break the token","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"clean":{"id":"clean","description":"Clean the configuration object\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[]},"download":{"id":"download","description":"Describe the command here\n...\nExtra documentation goes here\n","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"init":{"id":"init","description":"Create a new learning package: Book, Tutorial or Exercise","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"grading":{"name":"grading","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"login":{"id":"login","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"logout":{"id":"logout","description":"Describe the command here\n ...\n Extra documentation goes here\n ","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{},"args":[{"name":"package","description":"The unique string that identifies this package on learnpack","required":false,"hidden":false}]},"publish":{"id":"publish","description":"Builds the project by copying necessary files and directories into a zip file","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"start":{"id":"start","description":"Runs a small server with all the exercise instructions","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false},"port":{"name":"port","type":"option","char":"p","description":"server port"},"host":{"name":"host","type":"option","char":"h","description":"server host"},"disableGrading":{"name":"disableGrading","type":"boolean","char":"D","description":"disble grading functionality","allowNo":false},"watch":{"name":"watch","type":"boolean","char":"w","description":"Watch for file changes","allowNo":false},"editor":{"name":"editor","type":"option","char":"e","description":"[preview, extension]","options":["extension","preview"]},"version":{"name":"version","type":"option","char":"v","description":"E.g: 1.0.1"},"grading":{"name":"grading","type":"option","char":"g","description":"[isolated, incremental]","options":["isolated","incremental"]},"debug":{"name":"debug","type":"boolean","char":"d","description":"debugger mode for more verbage","allowNo":false}},"args":[]},"test":{"id":"test","description":"Test exercises","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[{"name":"exerciseSlug","description":"The name of the exercise to test","required":false,"hidden":false}]},"translate":{"id":"translate","description":"List all the lessons, the user is able of select many of them to translate to the given languages","pluginName":"@learnpack/learnpack","pluginType":"core","aliases":[],"flags":{"yes":{"name":"yes","type":"boolean","char":"y","description":"Skip all prompts and initialize an empty project","allowNo":false}},"args":[]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
4
- "version": "5.0.37",
4
+ "version": "5.0.38",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -1,312 +1,336 @@
1
- /* eslint-disable arrow-parens */
2
- /* eslint-disable unicorn/no-array-for-each */
3
- import { execSync } from "child_process"
4
- import { flags } from "@oclif/command"
5
- import SessionCommand from "../utils/SessionCommand"
6
- import SessionManager from "../managers/session"
7
- import * as fs from "fs"
8
- import * as path from "path"
9
- import * as archiver from "archiver"
10
- import axios from "axios"
11
- import FormData = require("form-data")
12
- import Console from "../utils/console"
13
- import {
14
- decompress,
15
- downloadEditor,
16
- checkIfDirectoryExists,
17
- } from "../managers/file"
18
- import api, { TAcademy } from "../utils/api"
19
- import * as prompts from "prompts"
20
-
21
- const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
22
- // const RIGOBOT_HOST =
23
- // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us116.gitpod.io"
24
- const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload"
25
-
26
- const runAudit = () => {
27
- try {
28
- Console.info("Running learnpack audit before publishing...")
29
- execSync("learnpack audit", { stdio: "inherit" })
30
- } catch (error) {
31
- Console.error("Failed to audit with learnpack")
32
-
33
- // Si `execSync` lanza un error, capturamos el mensaje de error
34
- if (error instanceof Error) {
35
- Console.error(error.message)
36
- } else {
37
- Console.error("Unknown error occurred")
38
- }
39
-
40
- // Detener la ejecución del comando si `learnpack publish` falla
41
- process.exit(1)
42
- }
43
-
44
- // Continuar con el proceso de build solo si `learnpack publish` fue exitoso
45
- Console.info(
46
- "Learnpack publish completed successfully. Proceeding with build..."
47
- )
48
- }
49
-
50
- const selectAcademy = async (academies: TAcademy[]) => {
51
- if (academies.length === 0) {
52
- return null
53
- }
54
-
55
- if (academies.length === 1) {
56
- return academies[0]
57
- }
58
-
59
- // prompts the user to select an academy to upload the assets
60
- Console.info("In which academy do you want to publish the asset?")
61
- const response = await prompts({
62
- type: "select",
63
- name: "academy",
64
- message: "Select an academy",
65
- choices: academies.map((academy) => ({
66
- title: academy.name,
67
- value: academy,
68
- })),
69
- })
70
- return response.academy
71
- }
72
-
73
- export default class BuildCommand extends SessionCommand {
74
- static description =
75
- "Builds the project by copying necessary files and directories into a zip file"
76
-
77
- static flags = {
78
- help: flags.help({ char: "h" }),
79
- }
80
-
81
- async init() {
82
- const { flags } = this.parse(BuildCommand)
83
- await this.initSession(flags)
84
- }
85
-
86
- async run() {
87
- const buildDir = path.join(process.cwd(), "build")
88
- // this.configManager?.clean()
89
-
90
- const configObject = this.configManager?.get()
91
-
92
- let sessionPayload = await SessionManager.getPayload()
93
- if (
94
- !sessionPayload ||
95
- !sessionPayload.rigobot ||
96
- (sessionPayload.token && !(await api.validateToken(sessionPayload.token)))
97
- ) {
98
- Console.error("You must be logged in to upload a LearnPack package")
99
- try {
100
- sessionPayload = await SessionManager.login()
101
- } catch (error) {
102
- Console.error("Error trying to authenticate")
103
- Console.error((error as TypeError).message || (error as string))
104
- }
105
- }
106
-
107
- const rigoToken = sessionPayload.rigobot.key
108
-
109
- if (configObject) {
110
- Console.info("Cleaning configuration files")
111
- this.configManager?.clean()
112
- // build exerises
113
- runAudit()
114
-
115
- Console.debug("Building exercises")
116
- this.configManager?.buildIndex()
117
- }
118
-
119
- // const academies = await api.listUserAcademies(sessionPayload.token)
120
- // // console.log(academies, "academies")
121
- // const academy = await selectAcademy(academies)
122
- // console.log(academy, "academy")
123
-
124
- // Read learn.json to get the slug
125
- const learnJsonPath = path.join(process.cwd(), "learn.json")
126
- if (!fs.existsSync(learnJsonPath)) {
127
- this.error("learn.json not found")
128
- }
129
-
130
- const learnJson = JSON.parse(fs.readFileSync(learnJsonPath, "utf-8"))
131
-
132
- const zipFilePath = path.join(process.cwd(), `${learnJson.slug}.zip`)
133
-
134
- // Ensure build directory exists
135
- if (!fs.existsSync(buildDir)) {
136
- fs.mkdirSync(buildDir)
137
- }
138
-
139
- if (configObject) {
140
- const { config } = configObject
141
- const appAlreadyExists = checkIfDirectoryExists(`${config?.dirPath}/_app`)
142
-
143
- if (!appAlreadyExists) {
144
- // download app and decompress
145
- await downloadEditor(
146
- config?.editor.version,
147
- `${config?.dirPath}/app.tar.gz`
148
- )
149
-
150
- Console.info("Decompressing LearnPack UI, this may take a minute...")
151
- await decompress(
152
- `${config?.dirPath}/app.tar.gz`,
153
- `${config?.dirPath}/_app/`
154
- )
155
- }
156
- }
157
-
158
- // Copy config.json
159
- const configPath = path.join(process.cwd(), ".learn", "config.json")
160
- if (fs.existsSync(configPath)) {
161
- fs.copyFileSync(configPath, path.join(buildDir, "config.json"))
162
- } else {
163
- this.error("config.json not found")
164
- }
165
-
166
- // Copy .learn/assets directory, if it exists else create it
167
- const assetsDir = path.join(process.cwd(), ".learn", "assets")
168
- if (fs.existsSync(assetsDir)) {
169
- this.copyDirectory(assetsDir, path.join(buildDir, ".learn", "assets"))
170
- } else {
171
- fs.mkdirSync(path.join(buildDir, ".learn", "assets"), { recursive: true })
172
- }
173
-
174
- // Copy .learn/_app directory files to the same level as config.json
175
- const appDir = path.join(process.cwd(), ".learn", "_app")
176
- if (fs.existsSync(appDir)) {
177
- this.copyDirectory(appDir, buildDir)
178
- } else {
179
- this.error(".learn/_app directory not found")
180
- }
181
-
182
- // After copying the _app directory
183
- const indexHtmlPath = path.join(appDir, "index.html")
184
- const buildIndexHtmlPath = path.join(buildDir, "index.html")
185
-
186
- if (fs.existsSync(indexHtmlPath)) {
187
- let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8")
188
-
189
- const description = learnJson.description.us || "LearnPack is awesome!"
190
- const title =
191
- learnJson.title.us || "LearnPack: Interactive Learning as a Service"
192
-
193
- const previewUrl =
194
- learnJson.preview ||
195
- "https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/public/learnpack.svg"
196
- // Replace placeholders and the <title>Old title </title> tag for a new tag with the title
197
- indexHtmlContent = indexHtmlContent
198
- .replace(/{{description}}/g, description)
199
- .replace(/<title>.*<\/title>/, `<title>${title}</title>`)
200
- .replace(/{{title}}/g, title)
201
- .replace(/{{preview}}/g, previewUrl)
202
-
203
- // Write the modified content to the build directory
204
- fs.writeFileSync(buildIndexHtmlPath, indexHtmlContent)
205
- } else {
206
- this.error("index.html not found in _app directory")
207
- }
208
-
209
- // Copy exercises directory
210
- const exercisesDir = path.join(process.cwd(), "exercises")
211
- const learnExercisesDir = path.join(process.cwd(), ".learn", "exercises")
212
-
213
- if (fs.existsSync(exercisesDir)) {
214
- this.copyDirectory(exercisesDir, path.join(buildDir, "exercises"))
215
- } else if (fs.existsSync(learnExercisesDir)) {
216
- this.copyDirectory(learnExercisesDir, path.join(buildDir, "exercises"))
217
- } else {
218
- this.error("exercises directory not found in either location")
219
- }
220
-
221
- // Copy learn.json
222
- fs.copyFileSync(learnJsonPath, path.join(buildDir, "learn.json"))
223
-
224
- // Create zip file
225
- const output = fs.createWriteStream(zipFilePath)
226
- const archive = archiver("zip", {
227
- zlib: { level: 9 },
228
- })
229
-
230
- output.on("close", async () => {
231
- this.log(
232
- `Build completed: ${zipFilePath} (${archive.pointer()} total bytes)`
233
- )
234
- // Remove build directory after zip is created
235
-
236
- Console.debug("Zip file saved in project root")
237
-
238
- const formData = new FormData()
239
- formData.append("file", fs.createReadStream(zipFilePath))
240
- formData.append("config", JSON.stringify(learnJson))
241
-
242
- try {
243
- const res = await axios.post(uploadZipEndpont, formData, {
244
- headers: {
245
- ...formData.getHeaders(),
246
- Authorization: `Token ${rigoToken}`,
247
- },
248
- })
249
- console.log(res.data)
250
- // Remove the zip file after uploading
251
- fs.unlinkSync(zipFilePath)
252
- this.removeDirectory(buildDir)
253
- } catch (error) {
254
- if (axios.isAxiosError(error)) {
255
- if (error.response && error.response.status === 403) {
256
- console.error("Error 403:", error.response.data.error)
257
- } else if (error.response && error.response.status === 400) {
258
- console.error(error.response.data.error)
259
- } else {
260
- console.error("Error uploading file:", error)
261
- }
262
- } else {
263
- console.error("Error uploading file:", error)
264
- }
265
-
266
- fs.unlinkSync(zipFilePath)
267
- this.removeDirectory(buildDir)
268
- }
269
- })
270
-
271
- archive.on("error", (err: any) => {
272
- throw err
273
- })
274
-
275
- archive.pipe(output)
276
- archive.directory(buildDir, false)
277
- await archive.finalize()
278
- }
279
-
280
- copyDirectory(src: string, dest: string) {
281
- if (!fs.existsSync(dest)) {
282
- fs.mkdirSync(dest, { recursive: true })
283
- }
284
-
285
- const entries = fs.readdirSync(src, { withFileTypes: true })
286
-
287
- for (const entry of entries) {
288
- const srcPath = path.join(src, entry.name)
289
- const destPath = path.join(dest, entry.name)
290
-
291
- if (entry.isDirectory()) {
292
- this.copyDirectory(srcPath, destPath)
293
- } else {
294
- fs.copyFileSync(srcPath, destPath)
295
- }
296
- }
297
- }
298
-
299
- removeDirectory(dir: string) {
300
- if (fs.existsSync(dir)) {
301
- fs.readdirSync(dir).forEach((file) => {
302
- const currentPath = path.join(dir, file)
303
- if (fs.lstatSync(currentPath).isDirectory()) {
304
- this.removeDirectory(currentPath)
305
- } else {
306
- fs.unlinkSync(currentPath)
307
- }
308
- })
309
- fs.rmdirSync(dir)
310
- }
311
- }
312
- }
1
+ /* eslint-disable arrow-parens */
2
+ /* eslint-disable unicorn/no-array-for-each */
3
+ import { execSync } from "child_process"
4
+ import { flags } from "@oclif/command"
5
+ import SessionCommand from "../utils/SessionCommand"
6
+ import SessionManager from "../managers/session"
7
+ import * as fs from "fs"
8
+ import * as path from "path"
9
+ import * as archiver from "archiver"
10
+ import axios from "axios"
11
+ import FormData = require("form-data")
12
+ import Console from "../utils/console"
13
+ import {
14
+ decompress,
15
+ downloadEditor,
16
+ checkIfDirectoryExists,
17
+ } from "../managers/file"
18
+ import api, { getConsumable, TAcademy } from "../utils/api"
19
+ import * as prompts from "prompts"
20
+ import { isValidRigoToken } from "../utils/rigoActions"
21
+
22
+ const RIGOBOT_HOST = "https://rigobot.herokuapp.com"
23
+ // const RIGOBOT_HOST =
24
+ // "https://8000-charlytoc-rigobot-bmwdeam7cev.ws-us116.gitpod.io"
25
+ const uploadZipEndpont = RIGOBOT_HOST + "/v1/learnpack/upload"
26
+
27
+ const runAudit = () => {
28
+ try {
29
+ Console.info("Running learnpack audit before publishing...")
30
+ execSync("learnpack audit", { stdio: "inherit" })
31
+ } catch (error) {
32
+ Console.error("Failed to audit with learnpack")
33
+
34
+ // Si `execSync` lanza un error, capturamos el mensaje de error
35
+ if (error instanceof Error) {
36
+ Console.error(error.message)
37
+ } else {
38
+ Console.error("Unknown error occurred")
39
+ }
40
+
41
+ // Detener la ejecución del comando si `learnpack publish` falla
42
+ process.exit(1)
43
+ }
44
+
45
+ // Continuar con el proceso de build solo si `learnpack publish` fue exitoso
46
+ Console.info(
47
+ "Learnpack publish completed successfully. Proceeding with build..."
48
+ )
49
+ }
50
+
51
+ const selectAcademy = async (academies: TAcademy[]) => {
52
+ if (academies.length === 0) {
53
+ return null
54
+ }
55
+
56
+ if (academies.length === 1) {
57
+ return academies[0]
58
+ }
59
+
60
+ // prompts the user to select an academy to upload the assets
61
+ Console.info("In which academy do you want to publish the asset?")
62
+ const response = await prompts({
63
+ type: "select",
64
+ name: "academy",
65
+ message: "Select an academy",
66
+ choices: academies.map((academy) => ({
67
+ title: academy.name,
68
+ value: academy,
69
+ })),
70
+ })
71
+ return response.academy
72
+ }
73
+
74
+ export default class BuildCommand extends SessionCommand {
75
+ static description =
76
+ "Builds the project by copying necessary files and directories into a zip file"
77
+
78
+ static flags = {
79
+ help: flags.help({ char: "h" }),
80
+ }
81
+
82
+ async init() {
83
+ const { flags } = this.parse(BuildCommand)
84
+ await this.initSession(flags)
85
+ }
86
+
87
+ async run() {
88
+ const buildDir = path.join(process.cwd(), "build")
89
+ // this.configManager?.clean()
90
+
91
+ const configObject = this.configManager?.get()
92
+
93
+ let sessionPayload = await SessionManager.getPayload()
94
+
95
+ const sessionExists = sessionPayload && sessionPayload.rigobot
96
+
97
+ const isValidToken =
98
+ sessionExists && sessionPayload.rigobot.key ?
99
+ await isValidRigoToken(sessionPayload.rigobot.key) :
100
+ false
101
+
102
+ const isValidBreathecodeToken =
103
+ sessionExists && sessionPayload.token ?
104
+ await api.validateToken(sessionPayload.token) :
105
+ false
106
+
107
+ if (!sessionExists || !isValidBreathecodeToken || !isValidToken) {
108
+ Console.info(
109
+ "Almost there! First you need to login with 4Geeks.com to use AI Generation tool for creators. You can create a new account here: https://4geeks.com/creators"
110
+ )
111
+ try {
112
+ sessionPayload = await SessionManager.login()
113
+ } catch (error) {
114
+ Console.error("Error trying to authenticate")
115
+ Console.error((error as TypeError).message || (error as string))
116
+ }
117
+ }
118
+
119
+ const rigoToken = sessionPayload.rigobot.key
120
+
121
+ const consumable = await getConsumable(
122
+ sessionPayload.token,
123
+ "learnpack-publish"
124
+ )
125
+
126
+ if (consumable.count === 0) {
127
+ Console.error(
128
+ "It seems you cannot publish tutorials. Make sure you creator subscription is up to date here: https://4geeks.com/profile/subscriptions. If you believe there is an issue you can always contact support@4geeks.com"
129
+ )
130
+ process.exit(1)
131
+ }
132
+
133
+ if (configObject) {
134
+ Console.info("Cleaning configuration files")
135
+ this.configManager?.clean()
136
+ // build exerises
137
+ runAudit()
138
+
139
+ Console.debug("Building exercises")
140
+ this.configManager?.buildIndex()
141
+ }
142
+
143
+ // const academies = await api.listUserAcademies(sessionPayload.token)
144
+ // // console.log(academies, "academies")
145
+ // const academy = await selectAcademy(academies)
146
+ // console.log(academy, "academy")
147
+
148
+ // Read learn.json to get the slug
149
+ const learnJsonPath = path.join(process.cwd(), "learn.json")
150
+ if (!fs.existsSync(learnJsonPath)) {
151
+ this.error("learn.json not found")
152
+ }
153
+
154
+ const learnJson = JSON.parse(fs.readFileSync(learnJsonPath, "utf-8"))
155
+
156
+ const zipFilePath = path.join(process.cwd(), `${learnJson.slug}.zip`)
157
+
158
+ // Ensure build directory exists
159
+ if (!fs.existsSync(buildDir)) {
160
+ fs.mkdirSync(buildDir)
161
+ }
162
+
163
+ if (configObject) {
164
+ const { config } = configObject
165
+ const appAlreadyExists = checkIfDirectoryExists(`${config?.dirPath}/_app`)
166
+
167
+ if (!appAlreadyExists) {
168
+ // download app and decompress
169
+ await downloadEditor(
170
+ config?.editor.version,
171
+ `${config?.dirPath}/app.tar.gz`
172
+ )
173
+
174
+ Console.info("Decompressing LearnPack UI, this may take a minute...")
175
+ await decompress(
176
+ `${config?.dirPath}/app.tar.gz`,
177
+ `${config?.dirPath}/_app/`
178
+ )
179
+ }
180
+ }
181
+
182
+ // Copy config.json
183
+ const configPath = path.join(process.cwd(), ".learn", "config.json")
184
+ if (fs.existsSync(configPath)) {
185
+ fs.copyFileSync(configPath, path.join(buildDir, "config.json"))
186
+ } else {
187
+ this.error("config.json not found")
188
+ }
189
+
190
+ // Copy .learn/assets directory, if it exists else create it
191
+ const assetsDir = path.join(process.cwd(), ".learn", "assets")
192
+ if (fs.existsSync(assetsDir)) {
193
+ this.copyDirectory(assetsDir, path.join(buildDir, ".learn", "assets"))
194
+ } else {
195
+ fs.mkdirSync(path.join(buildDir, ".learn", "assets"), { recursive: true })
196
+ }
197
+
198
+ // Copy .learn/_app directory files to the same level as config.json
199
+ const appDir = path.join(process.cwd(), ".learn", "_app")
200
+ if (fs.existsSync(appDir)) {
201
+ this.copyDirectory(appDir, buildDir)
202
+ } else {
203
+ this.error(".learn/_app directory not found")
204
+ }
205
+
206
+ // After copying the _app directory
207
+ const indexHtmlPath = path.join(appDir, "index.html")
208
+ const buildIndexHtmlPath = path.join(buildDir, "index.html")
209
+
210
+ if (fs.existsSync(indexHtmlPath)) {
211
+ let indexHtmlContent = fs.readFileSync(indexHtmlPath, "utf-8")
212
+
213
+ const description = learnJson.description.us || "LearnPack is awesome!"
214
+ const title =
215
+ learnJson.title.us || "LearnPack: Interactive Learning as a Service"
216
+
217
+ const previewUrl =
218
+ learnJson.preview ||
219
+ "https://raw.githubusercontent.com/learnpack/ide/refs/heads/master/public/learnpack.svg"
220
+ // Replace placeholders and the <title>Old title </title> tag for a new tag with the title
221
+ indexHtmlContent = indexHtmlContent
222
+ .replace(/{{description}}/g, description)
223
+ .replace(/<title>.*<\/title>/, `<title>${title}</title>`)
224
+ .replace(/{{title}}/g, title)
225
+ .replace(/{{preview}}/g, previewUrl)
226
+
227
+ // Write the modified content to the build directory
228
+ fs.writeFileSync(buildIndexHtmlPath, indexHtmlContent)
229
+ } else {
230
+ this.error("index.html not found in _app directory")
231
+ }
232
+
233
+ // Copy exercises directory
234
+ const exercisesDir = path.join(process.cwd(), "exercises")
235
+ const learnExercisesDir = path.join(process.cwd(), ".learn", "exercises")
236
+
237
+ if (fs.existsSync(exercisesDir)) {
238
+ this.copyDirectory(exercisesDir, path.join(buildDir, "exercises"))
239
+ } else if (fs.existsSync(learnExercisesDir)) {
240
+ this.copyDirectory(learnExercisesDir, path.join(buildDir, "exercises"))
241
+ } else {
242
+ this.error("exercises directory not found in either location")
243
+ }
244
+
245
+ // Copy learn.json
246
+ fs.copyFileSync(learnJsonPath, path.join(buildDir, "learn.json"))
247
+
248
+ // Create zip file
249
+ const output = fs.createWriteStream(zipFilePath)
250
+ const archive = archiver("zip", {
251
+ zlib: { level: 9 },
252
+ })
253
+
254
+ output.on("close", async () => {
255
+ this.log(
256
+ `Build completed: ${zipFilePath} (${archive.pointer()} total bytes)`
257
+ )
258
+ // Remove build directory after zip is created
259
+
260
+ Console.debug("Zip file saved in project root")
261
+
262
+ const formData = new FormData()
263
+ formData.append("file", fs.createReadStream(zipFilePath))
264
+ formData.append("config", JSON.stringify(learnJson))
265
+
266
+ try {
267
+ const res = await axios.post(uploadZipEndpont, formData, {
268
+ headers: {
269
+ ...formData.getHeaders(),
270
+ Authorization: `Token ${rigoToken}`,
271
+ },
272
+ })
273
+ console.log(res.data)
274
+ // Remove the zip file after uploading
275
+ fs.unlinkSync(zipFilePath)
276
+ this.removeDirectory(buildDir)
277
+ } catch (error) {
278
+ if (axios.isAxiosError(error)) {
279
+ if (error.response && error.response.status === 403) {
280
+ console.error("Error 403:", error.response.data.error)
281
+ } else if (error.response && error.response.status === 400) {
282
+ console.error(error.response.data.error)
283
+ } else {
284
+ console.error("Error uploading file:", error)
285
+ }
286
+ } else {
287
+ console.error("Error uploading file:", error)
288
+ }
289
+
290
+ fs.unlinkSync(zipFilePath)
291
+ this.removeDirectory(buildDir)
292
+ }
293
+ })
294
+
295
+ archive.on("error", (err: any) => {
296
+ throw err
297
+ })
298
+
299
+ archive.pipe(output)
300
+ archive.directory(buildDir, false)
301
+ await archive.finalize()
302
+ }
303
+
304
+ copyDirectory(src: string, dest: string) {
305
+ if (!fs.existsSync(dest)) {
306
+ fs.mkdirSync(dest, { recursive: true })
307
+ }
308
+
309
+ const entries = fs.readdirSync(src, { withFileTypes: true })
310
+
311
+ for (const entry of entries) {
312
+ const srcPath = path.join(src, entry.name)
313
+ const destPath = path.join(dest, entry.name)
314
+
315
+ if (entry.isDirectory()) {
316
+ this.copyDirectory(srcPath, destPath)
317
+ } else {
318
+ fs.copyFileSync(srcPath, destPath)
319
+ }
320
+ }
321
+ }
322
+
323
+ removeDirectory(dir: string) {
324
+ if (fs.existsSync(dir)) {
325
+ fs.readdirSync(dir).forEach((file) => {
326
+ const currentPath = path.join(dir, file)
327
+ if (fs.lstatSync(currentPath).isDirectory()) {
328
+ this.removeDirectory(currentPath)
329
+ } else {
330
+ fs.unlinkSync(currentPath)
331
+ }
332
+ })
333
+ fs.rmdirSync(dir)
334
+ }
335
+ }
336
+ }