gitorial-cli 1.1.0 → 2.1.0

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/src/constants.js DELETED
@@ -1,5 +0,0 @@
1
- const GITORIAL_METADATA = "gitorial_metadata.json";
2
- const REPACK_BRANCH = "gitorial-repacked";
3
- const UNPACKED_BRANCH = "gitorial-unpacked";
4
-
5
- module.exports = { GITORIAL_METADATA, REPACK_BRANCH, UNPACKED_BRANCH };
package/src/mdbook.js DELETED
@@ -1,482 +0,0 @@
1
- const simpleGit = require("simple-git");
2
- const fs = require("fs");
3
- const os = require('os');
4
- const path = require("path");
5
- const { copyAllContentsAndReplace, copyFilesAndDirectories, doesBranchExist } = require("./utils");
6
-
7
- async function mdbook(repoPath, inputBranch, outputBranch, subFolder) {
8
- try {
9
- // Create a new temporary folder
10
- const sourceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gitorial-source-'));
11
- const mdbookDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gitorial-mdbook-'));
12
-
13
- // Clone the repo into the source folder.
14
- const tempGit = simpleGit(sourceDir);
15
-
16
- // Resolve the full path to the local repository
17
- const resolvedRepoPath = path.resolve(repoPath);
18
- await tempGit.clone(resolvedRepoPath, '.', ['--branch', inputBranch]);
19
-
20
- await processGitorial(sourceDir, mdbookDir);
21
-
22
- let sourceGit = simpleGit(repoPath);
23
- // Check if the branch exists in the list of local branches
24
- const branchExists = await doesBranchExist(sourceGit, outputBranch)
25
-
26
- if (!branchExists) {
27
- // Create a fresh branch if it does not exist.
28
- await sourceGit.raw(['switch', '--orphan', outputBranch]);
29
- } else {
30
- // Checkout the current branch if it does.
31
- await sourceGit.checkout(outputBranch);
32
- }
33
-
34
- let outputFolder = repoPath;
35
- if (subFolder) {
36
- outputFolder = path.join(outputFolder, subFolder);
37
- }
38
-
39
- copyAllContentsAndReplace(mdbookDir, outputFolder);
40
-
41
- // Stage all files
42
- await sourceGit.add('*');
43
-
44
- // Create commit with commit message
45
- await sourceGit.commit(`mdBook generated from ${inputBranch}`);
46
-
47
- // Clean up source folder
48
- fs.rmSync(sourceDir, { recursive: true });
49
- fs.rmSync(mdbookDir, { recursive: true });
50
- console.log("Temporary files removed.");
51
-
52
- console.log("mdBook completed.");
53
- } catch (error) {
54
- console.error('Error:', error.message || error);
55
- }
56
- }
57
-
58
- async function processGitorial(sourceDir, mdbookDir) {
59
- const sourceGit = simpleGit(sourceDir);
60
-
61
- // Retrieve commit log
62
- const logs = await sourceGit.log();
63
-
64
- let stepCounter = 0;
65
- let templateFound = false;
66
- let solutionFound = false;
67
- let templateFiles = [];
68
- let solutionFiles = [];
69
- let sourceFiles = [];
70
- let stepNames = [];
71
-
72
- // Create a folder for each commit
73
- // Reverse to make the oldest commit first
74
- for ([index, log] of logs.all.reverse().entries()) {
75
- const commitHash = log.hash;
76
- const commitMessage = log.message;
77
-
78
- // These are the possible gitorial commit types
79
- const isReadme = commitMessage.toLowerCase().startsWith("readme: ");
80
- const isTemplate = commitMessage.toLowerCase().startsWith("template: ");
81
- const isSolution = commitMessage.toLowerCase().startsWith("solution: ");
82
- const isSection = commitMessage.toLowerCase().startsWith("section: ");
83
- const isAction = commitMessage.toLowerCase().startsWith("action: ");
84
- const isStartingTemplate = commitMessage.toLowerCase().startsWith("starting-template");
85
-
86
- // A step may not increment with a new commit, for example a `template` and `solution` happen in one step.
87
- let stepFolder = path.join(mdbookDir, stepCounter.toString());
88
- if (!fs.existsSync(stepFolder)) {
89
- fs.mkdirSync(stepFolder);
90
- }
91
-
92
- let sourceFolder = path.join(stepFolder, "source");
93
- let templateFolder = path.join(stepFolder, "template");
94
- let solutionFolder = path.join(stepFolder, "solution");
95
-
96
- // Default assumption is output is not a template or solution
97
- let outputFolder = sourceFolder;
98
-
99
- // We skip the starting template commit since it is only used for starting the project.
100
- if (isStartingTemplate) {
101
- continue;
102
- }
103
-
104
- if (isTemplate) {
105
- // Check there isn't a template already in queue
106
- if (templateFound) {
107
- console.error("A second template was found before a solution.");
108
- process.exit(1);
109
- }
110
-
111
- templateFound = true;
112
-
113
- // make step folder
114
- outputFolder = templateFolder;
115
- }
116
-
117
- if (isSolution) {
118
- // Check that there is a template in queue
119
- if (!templateFound) {
120
- console.error("No template was found for this solution.");
121
- process.exit(1);
122
- }
123
-
124
- // Check that a solution is not already found.
125
- if (solutionFound) {
126
- console.error("A second solution was found before a template.");
127
- process.exit(1);
128
- }
129
-
130
- solutionFound = true;
131
- outputFolder = solutionFolder;
132
- }
133
-
134
- fs.mkdirSync(outputFolder);
135
-
136
- // Checkout the commit
137
- console.log(`Checking out commit: ${commitHash}`);
138
- await sourceGit.checkout(commitHash)
139
-
140
- // Copy the contents to the commit folder
141
- copyFilesAndDirectories(sourceDir, outputFolder);
142
- console.log(`Contents of commit ${index} copied to ${outputFolder}`);
143
-
144
- let previousCommit = "HEAD~1";
145
- // This is the commit hash for an empty git project.
146
- let emptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
147
-
148
- if (index == 0) {
149
- previousCommit = emptyTree;
150
- }
151
-
152
- // Get the list of modified or created files in the commit
153
- const diffOutput = await sourceGit.diff(['--name-status', `${previousCommit}`, `HEAD`])
154
- // Get the raw diff between the previous commit and HEAD, excluding README.md
155
- const diffRaw = await sourceGit.diff([`${previousCommit}`, `HEAD`, ':(exclude)README.md']);
156
-
157
- // Create a raw output
158
- let diff_name = "changes.diff";
159
- if (isSolution) {
160
- diff_name = "solution.diff";
161
- } else if (isTemplate) {
162
- diff_name = "template.diff";
163
- }
164
- const diffFilePath = path.join(outputFolder, diff_name);
165
- fs.writeFileSync(diffFilePath, diffRaw);
166
-
167
- let fileStatus = diffOutput.split("\n").map((line) => {
168
- const [status, file] = line.split("\t");
169
- return { status, file };
170
- });
171
-
172
- if (isTemplate) {
173
- templateFiles = fileStatus;
174
- } else if (isSolution) {
175
- solutionFiles = fileStatus;
176
- } else {
177
- sourceFiles = fileStatus;
178
- }
179
-
180
- // Reset sanity check and increment step
181
- // Handle when both template and solution is found,
182
- // or when there is a step that is neither a template or solution
183
- if (
184
- (templateFound && solutionFound) ||
185
- (!templateFound && !solutionFound)
186
- ) {
187
- if (isReadme) {
188
- markdownContent = sectionMarkdown;
189
- } else if (isSection) {
190
- markdownContent = sectionMarkdown;
191
- stepNames.push({
192
- name: getStepName(sourceFolder),
193
- is_section: true,
194
- });
195
- } else if (templateFound) {
196
- markdownContent = templateMarkdown;
197
- let templateFileText = generateFileMarkdown("template", templateFiles);
198
- let solutionFileText = generateFileMarkdown("solution", solutionFiles);
199
- markdownContent = markdownContent.replace(
200
- "<!-- insert_template_files -->",
201
- templateFileText
202
- );
203
- markdownContent = markdownContent.replace(
204
- "<!-- insert_solution_files -->",
205
- solutionFileText
206
- );
207
-
208
- let diffText = generateDiffMarkdown("template");
209
- markdownContent = markdownContent.replace(
210
- "<!-- insert_diff_files -->",
211
- diffText
212
- );
213
-
214
- stepNames.push({
215
- name: getStepName(templateFolder),
216
- is_section: false,
217
- });
218
- } else if (isAction) {
219
- markdownContent = sourceMarkdown;
220
- let sourceFileText = generateFileMarkdown("source", sourceFiles);
221
- markdownContent = markdownContent.replace(
222
- "<!-- insert_source_files -->",
223
- sourceFileText
224
- );
225
-
226
- let diffText = generateDiffMarkdown("source");
227
- markdownContent = markdownContent.replace(
228
- "<!-- insert_diff_files -->",
229
- diffText
230
- );
231
-
232
- stepNames.push({
233
- name: getStepName(sourceFolder),
234
- is_section: false,
235
- });
236
- } else {
237
- console.error(`Unknown Gitorial Commit Type: ${commitMessage}`)
238
- }
239
- // Create a Markdown file in the commit folder
240
- const markdownFilePath = path.join(stepFolder, "README.md");
241
- fs.writeFileSync(markdownFilePath, markdownContent);
242
- stepCounter += 1;
243
- templateFound = false;
244
- solutionFound = false;
245
- }
246
- }
247
-
248
- generateSidebar(mdbookDir, stepNames);
249
-
250
- console.log("Finished Parsing.");
251
- }
252
-
253
- // Generate the markdown text for files.
254
- function generateFileMarkdown(type, files) {
255
- // type is expected to be one of "source", "solution", or "template"
256
- if (type != "solution" && type != "source" && type != "template") {
257
- process.exit(1);
258
- }
259
-
260
- let output = "";
261
-
262
- let parsedFiles = [];
263
- for (file of files) {
264
- if (!file.file) {
265
- continue;
266
- }
267
-
268
- // Skip all hidden folders, like `.gitorial`.
269
- if (file.file.startsWith(".")) {
270
- continue;
271
- }
272
-
273
- let filepath = `./${type}/${file.file}`;
274
- let filename = path.parse(filepath).base;
275
-
276
- // Skip README
277
- if (filename == "README.md") {
278
- continue;
279
- }
280
- // Skip hidden files
281
- if (filename.startsWith(".")) {
282
- continue;
283
- }
284
- // Skip Cargo.lock
285
- if (filename == "Cargo.lock") {
286
- continue;
287
- }
288
-
289
- let classStyle = `file-${type}`;
290
- if (file.status == "M") {
291
- classStyle += " file-modified";
292
- } else if (file.status == "A") {
293
- classStyle += " file-added";
294
- } else if (file.status == "D") {
295
- classStyle += " file-deleted";
296
- }
297
-
298
- let codeStyle = "text";
299
- let extname = path.extname(filepath);
300
- switch (extname) {
301
- case ".rs":
302
- codeStyle = "rust";
303
- break;
304
- case ".toml":
305
- codeStyle = "toml";
306
- break;
307
- case ".js":
308
- codeStyle = "js";
309
- break;
310
- case ".json":
311
- codeStyle = "json";
312
- break;
313
- case ".ts":
314
- codeStyle = "ts";
315
- break;
316
- default:
317
- codeStyle = "text";
318
- }
319
-
320
- parsedFiles.push({ filename: file.file, classStyle, codeStyle, filepath })
321
- }
322
-
323
- if (parsedFiles.length > 0) {
324
- output += `<div class="tab">\n`;
325
-
326
- for ([i, file] of parsedFiles.entries()) {
327
- output += `<button class="subtab tablinks ${file.classStyle}${i == 0 ? " active" : ""}" onclick="switchSubTab(event, '${file.filename}')" data-id="${file.filename}">${file.filename}</button>\n`;
328
- }
329
-
330
- output += `</div>\n`
331
-
332
- for ([i, file] of parsedFiles.entries()) {
333
- output += `<div id="${type}/${file.filename}" class="subtab tabcontent${i == 0 ? " active" : ""}" data-id="${file.filename}">\n\n`;
334
- output += `\`\`\`${file.codeStyle}\n{{#include ${file.filepath}}}\n\`\`\`\n\n`;
335
- output += `</div>\n\n`;
336
- }
337
- } else {
338
- output = "No files edited in this step.";
339
- }
340
-
341
- return output;
342
- }
343
-
344
- function generateDiffMarkdown(type) {
345
- let output = "";
346
-
347
- if (type == "template" || type == "solution") {
348
- output += solutionDiffMarkdown;
349
- } else {
350
- output += changesDiffMarkdown;
351
- }
352
-
353
- return output;
354
- }
355
-
356
- let solutionDiffMarkdown = `
357
- <div class="tab">
358
- <button class="difftab tablinks active" onclick="switchDiff(event, 'template.diff')" data-id="template.diff">template.diff</button>
359
- <button class="difftab tablinks" onclick="switchDiff(event, 'solution.diff')" data-id="solution.diff">solution.diff</button>
360
- </div>
361
- <div id="template.diff" class="difftab tabcontent active" data-id="template.diff">
362
-
363
- \`\`\`diff\n{{#include ./template/template.diff}}\n\`\`\`
364
-
365
- </div>
366
- <div id="solution.diff" class="difftab tabcontent" data-id="solution.diff">
367
-
368
- \`\`\`diff\n{{#include ./solution/solution.diff}}\n\`\`\`
369
-
370
- </div>`;
371
-
372
- let changesDiffMarkdown = `
373
- <div class="tab">
374
- <button class="difftab tablinks active" onclick="switchDiff(event, 'changes.diff')" data-id="changes.diff">changes.diff</button>
375
- </div>
376
- <div id="changes.diff" class="difftab tabcontent active" data-id="changes.diff">
377
-
378
- \`\`\`diff\n{{#include ./source/changes.diff}}\n\`\`\`
379
-
380
- </div>`;
381
-
382
- let templateMarkdown = `
383
- <div class="content-row">
384
- <div class="content-col">
385
-
386
- {{#include ./template/README.md}}
387
-
388
- </div>
389
-
390
- <div class="content-col">
391
-
392
- <div class="tab">
393
- <button class="maintab tablinks active" onclick="switchMainTab(event, 'Template')">Template</button>
394
- <button class="maintab tablinks" onclick="switchMainTab(event, 'Solution')">Solution</button>
395
- <button class="maintab tablinks" onclick="switchMainTab(event, 'Diff')">Diff</button>
396
- </div>
397
-
398
- <div id="Template" class="maintab tabcontent active">
399
-
400
- <!-- insert_template_files -->
401
-
402
- </div>
403
-
404
- <div id="Solution" class="maintab tabcontent">
405
-
406
- <!-- insert_solution_files -->
407
-
408
- </div>
409
-
410
- <div id="Diff" class="maintab tabcontent">
411
-
412
- <!-- insert_diff_files -->
413
-
414
- </div>
415
-
416
- </div>
417
- </div>
418
- `;
419
-
420
- let sourceMarkdown = `
421
- <div class="content-row">
422
- <div class="content-col">
423
-
424
- {{#include ./source/README.md}}
425
-
426
- </div>
427
- <div class="content-col">
428
-
429
- <div class="tab">
430
- <button class="maintab tablinks active" onclick="switchMainTab(event, 'Source')">Source</button>
431
- <button class="maintab tablinks" onclick="switchMainTab(event, 'Diff')">Diff</button>
432
- </div>
433
-
434
- <div id="Source" class="maintab tabcontent active">
435
-
436
- <!-- insert_source_files -->
437
-
438
- </div>
439
-
440
- <div id="Diff" class="maintab tabcontent">
441
-
442
- <!-- insert_diff_files -->
443
-
444
- </div>
445
-
446
- </div>
447
- </div>
448
- `;
449
-
450
- let sectionMarkdown = `
451
- <div class="content-section">
452
-
453
- {{#include ./source/README.md}}
454
-
455
- </div>
456
- `;
457
-
458
- function getStepName(folder) {
459
- const filePath = path.join(folder, "README.md");
460
- const markdownContent = fs.readFileSync(filePath, "utf8");
461
- const titleMatch = markdownContent.match(/^#\s+(.*)/m);
462
- if (titleMatch) {
463
- return titleMatch[1];
464
- } else {
465
- console.error(`Error getting markdown title.`);
466
- process.exit(1);
467
- }
468
- }
469
-
470
- function generateSidebar(mdbookDir, steps) {
471
- const sidebarFilePath = path.join(mdbookDir, "SUMMARY.md");
472
- let output = "";
473
- steps.forEach(({ name, is_section }, index) => {
474
- if (!is_section) {
475
- output += ` `;
476
- }
477
- output += `- [${index}. ${name}](${index}/README.md)\n`;
478
- });
479
- fs.writeFileSync(sidebarFilePath, output);
480
- }
481
-
482
- module.exports = mdbook;
package/src/repack.js DELETED
@@ -1,85 +0,0 @@
1
- const simpleGit = require('simple-git');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { GITORIAL_METADATA } = require('./constants');
6
- const { copyAllContentsAndReplace } = require('./utils')
7
-
8
- async function repack(repoPath, inputBranch, outputBranch, subFolder, force) {
9
- try {
10
- const git = simpleGit(repoPath);
11
-
12
- if (force) {
13
- let saveBranch = `${outputBranch}-__gitorial-old`;
14
- // Delete the existing `__gitorial-old` branch if it exists
15
- try {
16
- await git.raw(['branch', '-D', saveBranch]);
17
- } catch (error) {
18
- // Ignore the error if the branch does not exist
19
- }
20
- // Move the output branch to the save branch
21
- try {
22
- await git.branch(['-m', outputBranch, saveBranch]);
23
- } catch (error) {
24
- // Ignore the error if the branch does not exist
25
- }
26
- await git.raw(['switch', '--orphan', outputBranch]);
27
- } else {
28
- await git.raw(['switch', '--orphan', outputBranch]);
29
- }
30
-
31
- const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gitorial-repack-'));
32
- await git.clone(repoPath, tempDir, ['--branch', inputBranch]);
33
-
34
- let unpackedDir = tempDir;
35
- if (subFolder) {
36
- unpackedDir = path.join(unpackedDir, subFolder)
37
- }
38
-
39
- // Get list of steps
40
- const steps = fs.readdirSync(unpackedDir)
41
- .filter(item => fs.statSync(path.join(unpackedDir, item)).isDirectory())
42
- .filter(item => item != '.git') // skip the git directory
43
- .sort((a, b) => parseInt(a) - parseInt(b)); // Sort folders numerically
44
-
45
- for (const step of steps) {
46
- const stepFolderPath = path.join(unpackedDir, step);
47
-
48
- // Read commit message from GITORIAL_METADATA
49
- const commitInfoPath = path.join(stepFolderPath, GITORIAL_METADATA);
50
- const commitInfo = JSON.parse(fs.readFileSync(commitInfoPath, 'utf-8'));
51
- const commitMessage = commitInfo.commitMessage;
52
-
53
- // Copy files from numbered folder to repo path
54
- copyAllContentsAndReplace(stepFolderPath, repoPath);
55
-
56
- // Stage all files
57
- await git.add('*');
58
-
59
- // Remove GITORIAL_METADATA
60
- await git.reset(GITORIAL_METADATA);
61
- await git.rm(GITORIAL_METADATA);
62
-
63
- // Create commit with commit message
64
- await git.commit(commitMessage);
65
- console.log(`Commit created for step ${step} with message: ${commitMessage}`);
66
-
67
- // We want to tag the `starting-template` so it can be easily referenced.
68
- if (commitMessage == 'starting-template') {
69
- await git.raw(['tag', 'starting-template', '--force']);
70
- console.log(`Added ${commitMessage} tag.`);
71
- }
72
- }
73
-
74
- // Clean up temp folder
75
- fs.rmSync(tempDir, { recursive: true });
76
- console.log("Temporary files removed.");
77
-
78
- console.log('Commits created successfully.')
79
-
80
- } catch (error) {
81
- console.error('Error:', error.message || error);
82
- }
83
- }
84
-
85
- module.exports = repack;
package/src/unpack.js DELETED
@@ -1,88 +0,0 @@
1
- const simpleGit = require('simple-git');
2
- const fs = require('fs');
3
- const os = require('os');
4
- const path = require('path');
5
- const { GITORIAL_METADATA } = require('./constants');
6
- const { copyAllContentsAndReplace, doesBranchExist, copyFilesAndDirectories } = require('./utils')
7
-
8
- async function unpack(repoPath, inputBranch, outputBranch, outputSubFolder) {
9
- try {
10
- // Create a new temporary folder
11
- const sourceDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gitorial-source-'));
12
- const unpackedDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gitorial-unpacked-'));
13
-
14
- // Clone the repo into the source folder.
15
- const tempGit = simpleGit(sourceDir);
16
-
17
- // Resolve the full path to the local repository
18
- const resolvedRepoPath = path.resolve(repoPath);
19
- await tempGit.clone(resolvedRepoPath, '.', ['--branch', inputBranch]);
20
-
21
- // Retrieve commit log
22
- const logs = await tempGit.log();
23
-
24
- // Create a folder for each commit
25
- // Reverse to make the oldest commit first
26
- for ([index, log] of logs.all.reverse().entries()) {
27
- const commitHash = log.hash;
28
- const commitMessage = log.message;
29
-
30
- let stepFolder = path.join(unpackedDir, index.toString());
31
-
32
- // Checkout the commit
33
- console.log(`Checking out commit: ${commitHash}`);
34
- await tempGit.checkout(commitHash);
35
-
36
- // Copy the contents to the commit folder
37
- copyFilesAndDirectories(sourceDir, stepFolder);
38
- console.log(`Contents copied from ${sourceDir} to ${stepFolder}`);
39
-
40
- // Create a JSON file in the commit folder
41
- const jsonFilePath = path.join(stepFolder, GITORIAL_METADATA);
42
- const commitInfoObject = {
43
- "_Note": "This file will not be included in your final gitorial.",
44
- commitMessage,
45
- };
46
-
47
- fs.writeFileSync(jsonFilePath, JSON.stringify(commitInfoObject, null, 2));
48
- }
49
-
50
- let sourceGit = simpleGit(repoPath);
51
-
52
- // Check if the branch exists in the list of local branches
53
- const branchExists = await doesBranchExist(sourceGit, outputBranch)
54
-
55
- if (!branchExists) {
56
- // Create a fresh branch if it does not exist.
57
- await sourceGit.raw(['switch', '--orphan', outputBranch]);
58
- } else {
59
- // Checkout the current branch if it does.
60
- await sourceGit.checkout(outputBranch)
61
- }
62
-
63
- let outputFolder = repoPath;
64
- if (outputSubFolder) {
65
- outputFolder = path.join(repoPath, outputSubFolder)
66
- }
67
-
68
- copyAllContentsAndReplace(unpackedDir, outputFolder);
69
-
70
- // Stage all files
71
- await sourceGit.add('*');
72
-
73
- // Create commit with commit message
74
- await sourceGit.commit(`Unpacked from ${inputBranch}`);
75
-
76
- // Clean up source folder
77
- fs.rmSync(sourceDir, { recursive: true });
78
- fs.rmSync(unpackedDir, { recursive: true });
79
- console.log("Temporary files removed.");
80
-
81
- console.log("Process completed.");
82
- } catch (error) {
83
- console.error('Error:', error.message || error);
84
- }
85
-
86
- }
87
-
88
- module.exports = unpack;