From 907d2837df72569b207abb7bec2e7d886bad3175 Mon Sep 17 00:00:00 2001 From: shmck Date: Wed, 3 Jun 2020 22:35:40 -0700 Subject: [PATCH 1/2] validate commit order Signed-off-by: shmck --- src/build.ts | 1 - src/utils/commitOrder.ts | 51 +++++++++++++++++++++++++++++++++++++++ src/utils/commits.ts | 11 ++++++--- src/utils/parse.ts | 4 +-- src/validate.ts | 17 ++++++++++--- tests/commitOrder.test.ts | 47 ++++++++++++++++++++++++++++++++++++ tests/validate.test.ts | 2 +- 7 files changed, 122 insertions(+), 11 deletions(-) create mode 100644 src/utils/commitOrder.ts create mode 100644 tests/commitOrder.test.ts diff --git a/src/build.ts b/src/build.ts index 3f5662a..2143f96 100644 --- a/src/build.ts +++ b/src/build.ts @@ -1,6 +1,5 @@ import * as yamlParser from "js-yaml"; import * as path from "path"; -import * as _ from "lodash"; import * as fs from "fs"; import * as util from "util"; import { parse } from "./utils/parse"; diff --git a/src/utils/commitOrder.ts b/src/utils/commitOrder.ts new file mode 100644 index 0000000..56f3de2 --- /dev/null +++ b/src/utils/commitOrder.ts @@ -0,0 +1,51 @@ +// should flag commits that are out of order based on the previous commit +// position is a string like 'INIT', 'L1', 'L1S1' +export function addToCommitOrder(position: string) { + // add position to list +} + +export function validateCommitOrder(positions: string[]): boolean { + // loop over positions + const errors: number[] = []; + let previous = { level: 0, step: 0 }; + let current = { level: 0, step: 0 }; + positions.forEach((position: string, index: number) => { + if (position === "INIT") { + if (previous.level !== 0 && previous.step !== 0) { + console.log("ERROR HERE"); + errors.push(index); + } + current = { level: 0, step: 0 }; + return; + } else { + const levelMatch = position.match(/^L([0-9])+$/); + const stepMatch = position.match(/^L([0-9])+S([0-9])+$/); + if (levelMatch) { + // allows next level or step + const [_, levelString] = levelMatch; + const level = Number(levelString); + current = { level, step: 0 }; + } else if (stepMatch) { + // allows next level or step + const [_, levelString, stepString] = stepMatch; + const level = Number(levelString); + const step = Number(stepString); + current = { level, step }; + } else { + // error + console.error(`Invalid commit position: ${position}`); + return; + } + if ( + current.level < previous.level || + (current.level === previous.level && current.step < previous.step) + ) { + errors.push(index); + } + } + previous = current; + }); + + // TODO: log errors based on index + return !errors.length; +} diff --git a/src/utils/commits.ts b/src/utils/commits.ts index 74132de..2072993 100644 --- a/src/utils/commits.ts +++ b/src/utils/commits.ts @@ -2,7 +2,7 @@ import * as fs from "fs"; import util from "util"; import * as path from "path"; import gitP, { SimpleGit } from "simple-git/promise"; -import * as T from "../../typings/tutorial"; +import { addToCommitOrder, validateCommitOrder } from "./commitOrder"; const mkdir = util.promisify(fs.mkdir); const exists = util.promisify(fs.exists); @@ -21,19 +21,20 @@ export async function getCommits({ }: GetCommitOptions): Promise { const git: SimpleGit = gitP(localDir); + // check that a repo is created const isRepo = await git.checkIsRepo(); - if (!isRepo) { throw new Error("No git repo provided"); } + // setup .tmp directory const tmpDir = path.join(localDir, ".tmp"); - const tmpDirExists = await exists(tmpDir); if (tmpDirExists) { await rmdir(tmpDir, { recursive: true }); } await mkdir(tmpDir); + const tempGit = gitP(tmpDir); await tempGit.clone(localDir, tmpDir); @@ -57,6 +58,7 @@ export async function getCommits({ // Load all logs const logs = await git.log(); + const positions: string[] = []; for (const commit of logs.all) { const matches = commit.message.match( @@ -73,6 +75,7 @@ export async function getCommits({ // add to the list commits[position].push(commit.hash); } + positions.push(position); } else { const initMatches = commit.message.match(/^INIT/); if (initMatches && initMatches.length) { @@ -83,9 +86,11 @@ export async function getCommits({ // add to the list commits.INIT.push(commit.hash); } + positions.push("INIT"); } } } + validateCommitOrder(positions); } catch (e) { console.error("Error with checkout or commit matching"); throw new Error(e.message); diff --git a/src/utils/parse.ts b/src/utils/parse.ts index afa1d4f..59886bc 100644 --- a/src/utils/parse.ts +++ b/src/utils/parse.ts @@ -1,4 +1,4 @@ -import * as _ from "lodash"; +import { truncate } from "lodash"; import { CommitLogObject } from "./commits"; import * as T from "../../typings/tutorial"; @@ -68,7 +68,7 @@ export function parseMdContent(md: string): TutorialFrame | never { title: levelTitle.trim(), summary: levelSummary ? levelSummary.trim() - : _.truncate(levelContent.trim(), { length: 80, omission: "..." }), + : truncate(levelContent.trim(), { length: 80, omission: "..." }), content: levelContent.trim(), }; } else { diff --git a/src/validate.ts b/src/validate.ts index 04ab543..029a501 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -1,12 +1,22 @@ +import * as path from "path"; +import * as fs from "fs"; +import util from "util"; +import gitP, { SimpleGit } from "simple-git/promise"; +import { getCommits, CommitLogObject } from "./utils/commits"; + +const mkdir = util.promisify(fs.mkdir); +const exists = util.promisify(fs.exists); +const rmdir = util.promisify(fs.rmdir); + async function validate(args: string[]) { // dir - default . const dir = !args.length || args[0].match(/^-/) ? "." : args[0]; console.warn("Not yet implemented. Coming soon"); - return; - // setup .tmp directory - // git clone branch + const localDir = path.join(process.cwd(), dir); + const codeBranch = ""; + const commits = getCommits({ localDir, codeBranch }); // VALIDATE SKELETON WITH COMMITS // parse tutorial skeleton for order and commands @@ -37,7 +47,6 @@ async function validate(args: string[]) { // on error, show level/step & error message // CLEANUP - // finally: remove .tmp directory } export default validate; diff --git a/tests/commitOrder.test.ts b/tests/commitOrder.test.ts new file mode 100644 index 0000000..ac483e0 --- /dev/null +++ b/tests/commitOrder.test.ts @@ -0,0 +1,47 @@ +import { validateCommitOrder } from "../src/utils/commitOrder"; + +describe("commitOrder", () => { + it("should return true if order is valid", () => { + const positions = ["INIT", "L1", "L1S1", "L1S2", "L2", "L2S1"]; + const result = validateCommitOrder(positions); + expect(result).toBe(true); + }); + it("should return true if valid with duplicates", () => { + const positions = [ + "INIT", + "INIT", + "L1", + "L1", + "L1S1", + "L1S1", + "L1S2", + "L1S2", + "L2", + "L2", + "L2S1", + "L2S1", + ]; + const result = validateCommitOrder(positions); + expect(result).toBe(true); + }); + it("should return false if INIT is out of order", () => { + const positions = ["INIT", "L1", "L1S1", "L1S2", "INIT", "L2", "L2S1"]; + const result = validateCommitOrder(positions); + expect(result).toBe(false); + }); + it("should return false if level after step is out of order", () => { + const positions = ["INIT", "L1", "L1S1", "L1S2", "L2S1", "L2"]; + const result = validateCommitOrder(positions); + expect(result).toBe(false); + }); + it("should return false if level is out of order", () => { + const positions = ["INIT", "L1", "L3", "L2"]; + const result = validateCommitOrder(positions); + expect(result).toBe(false); + }); + it("should return false if step is out of order", () => { + const positions = ["INIT", "L1", "L1S1", "L1S3", "L1S2"]; + const result = validateCommitOrder(positions); + expect(result).toBe(false); + }); +}); diff --git a/tests/validate.test.ts b/tests/validate.test.ts index 66626b5..1355fae 100644 --- a/tests/validate.test.ts +++ b/tests/validate.test.ts @@ -1,5 +1,5 @@ import * as T from "../typings/tutorial"; -import { validateSchema } from "../src/utils/validate"; +import { validateSchema } from "../src/utils/validateSchema"; describe("validate", () => { it("should reject an empty tutorial", () => { From 57946077282b91ac35c7a4fa84a13fc1900a4666 Mon Sep 17 00:00:00 2001 From: shmck Date: Wed, 3 Jun 2020 22:39:39 -0700 Subject: [PATCH 2/2] log commit order warnings Signed-off-by: shmck --- src/utils/commitOrder.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/utils/commitOrder.ts b/src/utils/commitOrder.ts index 56f3de2..dae894d 100644 --- a/src/utils/commitOrder.ts +++ b/src/utils/commitOrder.ts @@ -46,6 +46,15 @@ export function validateCommitOrder(positions: string[]): boolean { previous = current; }); - // TODO: log errors based on index + if (errors.length) { + console.warn("Found commit positions out of order"); + positions.forEach((position, index) => { + if (errors.includes(index)) { + console.warn(`${position} <-`); + } else { + console.log(position); + } + }); + } return !errors.length; }