Skip to content

Commit 89d7dd5

Browse files
authored
Merge pull request #26 from coderoad/feature/validate-commit-order
Feature/validate commit order
2 parents 8a3aef5 + 5794607 commit 89d7dd5

File tree

7 files changed

+131
-11
lines changed

7 files changed

+131
-11
lines changed

Diff for: src/build.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import * as yamlParser from "js-yaml";
22
import * as path from "path";
3-
import * as _ from "lodash";
43
import * as fs from "fs";
54
import * as util from "util";
65
import { parse } from "./utils/parse";

Diff for: src/utils/commitOrder.ts

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// should flag commits that are out of order based on the previous commit
2+
// position is a string like 'INIT', 'L1', 'L1S1'
3+
export function addToCommitOrder(position: string) {
4+
// add position to list
5+
}
6+
7+
export function validateCommitOrder(positions: string[]): boolean {
8+
// loop over positions
9+
const errors: number[] = [];
10+
let previous = { level: 0, step: 0 };
11+
let current = { level: 0, step: 0 };
12+
positions.forEach((position: string, index: number) => {
13+
if (position === "INIT") {
14+
if (previous.level !== 0 && previous.step !== 0) {
15+
console.log("ERROR HERE");
16+
errors.push(index);
17+
}
18+
current = { level: 0, step: 0 };
19+
return;
20+
} else {
21+
const levelMatch = position.match(/^L([0-9])+$/);
22+
const stepMatch = position.match(/^L([0-9])+S([0-9])+$/);
23+
if (levelMatch) {
24+
// allows next level or step
25+
const [_, levelString] = levelMatch;
26+
const level = Number(levelString);
27+
current = { level, step: 0 };
28+
} else if (stepMatch) {
29+
// allows next level or step
30+
const [_, levelString, stepString] = stepMatch;
31+
const level = Number(levelString);
32+
const step = Number(stepString);
33+
current = { level, step };
34+
} else {
35+
// error
36+
console.error(`Invalid commit position: ${position}`);
37+
return;
38+
}
39+
if (
40+
current.level < previous.level ||
41+
(current.level === previous.level && current.step < previous.step)
42+
) {
43+
errors.push(index);
44+
}
45+
}
46+
previous = current;
47+
});
48+
49+
if (errors.length) {
50+
console.warn("Found commit positions out of order");
51+
positions.forEach((position, index) => {
52+
if (errors.includes(index)) {
53+
console.warn(`${position} <-`);
54+
} else {
55+
console.log(position);
56+
}
57+
});
58+
}
59+
return !errors.length;
60+
}

Diff for: src/utils/commits.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as fs from "fs";
22
import util from "util";
33
import * as path from "path";
44
import gitP, { SimpleGit } from "simple-git/promise";
5-
import * as T from "../../typings/tutorial";
5+
import { addToCommitOrder, validateCommitOrder } from "./commitOrder";
66

77
const mkdir = util.promisify(fs.mkdir);
88
const exists = util.promisify(fs.exists);
@@ -21,19 +21,20 @@ export async function getCommits({
2121
}: GetCommitOptions): Promise<CommitLogObject> {
2222
const git: SimpleGit = gitP(localDir);
2323

24+
// check that a repo is created
2425
const isRepo = await git.checkIsRepo();
25-
2626
if (!isRepo) {
2727
throw new Error("No git repo provided");
2828
}
2929

30+
// setup .tmp directory
3031
const tmpDir = path.join(localDir, ".tmp");
31-
3232
const tmpDirExists = await exists(tmpDir);
3333
if (tmpDirExists) {
3434
await rmdir(tmpDir, { recursive: true });
3535
}
3636
await mkdir(tmpDir);
37+
3738
const tempGit = gitP(tmpDir);
3839
await tempGit.clone(localDir, tmpDir);
3940

@@ -57,6 +58,7 @@ export async function getCommits({
5758

5859
// Load all logs
5960
const logs = await git.log();
61+
const positions: string[] = [];
6062

6163
for (const commit of logs.all) {
6264
const matches = commit.message.match(
@@ -73,6 +75,7 @@ export async function getCommits({
7375
// add to the list
7476
commits[position].push(commit.hash);
7577
}
78+
positions.push(position);
7679
} else {
7780
const initMatches = commit.message.match(/^INIT/);
7881
if (initMatches && initMatches.length) {
@@ -83,9 +86,11 @@ export async function getCommits({
8386
// add to the list
8487
commits.INIT.push(commit.hash);
8588
}
89+
positions.push("INIT");
8690
}
8791
}
8892
}
93+
validateCommitOrder(positions);
8994
} catch (e) {
9095
console.error("Error with checkout or commit matching");
9196
throw new Error(e.message);

Diff for: src/utils/parse.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import * as _ from "lodash";
1+
import { truncate } from "lodash";
22
import { CommitLogObject } from "./commits";
33
import * as T from "../../typings/tutorial";
44

@@ -68,7 +68,7 @@ export function parseMdContent(md: string): TutorialFrame | never {
6868
title: levelTitle.trim(),
6969
summary: levelSummary
7070
? levelSummary.trim()
71-
: _.truncate(levelContent.trim(), { length: 80, omission: "..." }),
71+
: truncate(levelContent.trim(), { length: 80, omission: "..." }),
7272
content: levelContent.trim(),
7373
};
7474
} else {

Diff for: src/validate.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1+
import * as path from "path";
2+
import * as fs from "fs";
3+
import util from "util";
4+
import gitP, { SimpleGit } from "simple-git/promise";
5+
import { getCommits, CommitLogObject } from "./utils/commits";
6+
7+
const mkdir = util.promisify(fs.mkdir);
8+
const exists = util.promisify(fs.exists);
9+
const rmdir = util.promisify(fs.rmdir);
10+
111
async function validate(args: string[]) {
212
// dir - default .
313
const dir = !args.length || args[0].match(/^-/) ? "." : args[0];
414
console.warn("Not yet implemented. Coming soon");
5-
return;
615

7-
// setup .tmp directory
8-
// git clone branch
16+
const localDir = path.join(process.cwd(), dir);
17+
const codeBranch = "";
918

19+
const commits = getCommits({ localDir, codeBranch });
1020
// VALIDATE SKELETON WITH COMMITS
1121
// parse tutorial skeleton for order and commands
1222

@@ -37,7 +47,6 @@ async function validate(args: string[]) {
3747
// on error, show level/step & error message
3848

3949
// CLEANUP
40-
// finally: remove .tmp directory
4150
}
4251

4352
export default validate;

Diff for: tests/commitOrder.test.ts

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { validateCommitOrder } from "../src/utils/commitOrder";
2+
3+
describe("commitOrder", () => {
4+
it("should return true if order is valid", () => {
5+
const positions = ["INIT", "L1", "L1S1", "L1S2", "L2", "L2S1"];
6+
const result = validateCommitOrder(positions);
7+
expect(result).toBe(true);
8+
});
9+
it("should return true if valid with duplicates", () => {
10+
const positions = [
11+
"INIT",
12+
"INIT",
13+
"L1",
14+
"L1",
15+
"L1S1",
16+
"L1S1",
17+
"L1S2",
18+
"L1S2",
19+
"L2",
20+
"L2",
21+
"L2S1",
22+
"L2S1",
23+
];
24+
const result = validateCommitOrder(positions);
25+
expect(result).toBe(true);
26+
});
27+
it("should return false if INIT is out of order", () => {
28+
const positions = ["INIT", "L1", "L1S1", "L1S2", "INIT", "L2", "L2S1"];
29+
const result = validateCommitOrder(positions);
30+
expect(result).toBe(false);
31+
});
32+
it("should return false if level after step is out of order", () => {
33+
const positions = ["INIT", "L1", "L1S1", "L1S2", "L2S1", "L2"];
34+
const result = validateCommitOrder(positions);
35+
expect(result).toBe(false);
36+
});
37+
it("should return false if level is out of order", () => {
38+
const positions = ["INIT", "L1", "L3", "L2"];
39+
const result = validateCommitOrder(positions);
40+
expect(result).toBe(false);
41+
});
42+
it("should return false if step is out of order", () => {
43+
const positions = ["INIT", "L1", "L1S1", "L1S3", "L1S2"];
44+
const result = validateCommitOrder(positions);
45+
expect(result).toBe(false);
46+
});
47+
});

Diff for: tests/validate.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as T from "../typings/tutorial";
2-
import { validateSchema } from "../src/utils/validate";
2+
import { validateSchema } from "../src/utils/validateSchema";
33

44
describe("validate", () => {
55
it("should reject an empty tutorial", () => {

0 commit comments

Comments
 (0)