From 5b1a6dd4723f44649d6db1cdb34fcf3844bb7f52 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 08:15:22 -0700 Subject: [PATCH 01/11] validate progress Signed-off-by: shmck --- src/validate.ts | 67 +++++++++++--------- tests/{validate.test.ts => tutorial.test.ts} | 0 2 files changed, 37 insertions(+), 30 deletions(-) rename tests/{validate.test.ts => tutorial.test.ts} (100%) diff --git a/src/validate.ts b/src/validate.ts index 4da3048..4075640 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -5,6 +5,7 @@ import * as yamlParser from "js-yaml"; import { getArg } from "./utils/args"; import gitP, { SimpleGit } from "simple-git/promise"; import { getCommits, CommitLogObject } from "./utils/commits"; +import simplegit from "simple-git/promise"; const mkdir = util.promisify(fs.mkdir); const exists = util.promisify(fs.exists); @@ -24,11 +25,13 @@ async function validate(args: string[]) { const _yaml = await read(path.join(localDir, options.yaml), "utf8"); // parse yaml config - let config; + let skeleton; try { - config = yamlParser.load(_yaml); + skeleton = yamlParser.load(_yaml); + + console.log("config", skeleton); // TODO: validate yaml - if (!config || !config.length) { + if (!skeleton) { throw new Error("Invalid yaml file contents"); } } catch (e) { @@ -36,40 +39,44 @@ async function validate(args: string[]) { console.error(e.message); } - const codeBranch: string = config.config.repo.branch; - - // VALIDATE SKELETON WITH COMMITS - const commits = getCommits({ localDir, codeBranch }); + const codeBranch: string = skeleton.config.repo.branch; - // parse tutorial skeleton for order and commands + // validate commits + const commits = await getCommits({ localDir, codeBranch }); - // on error, warn missing level/step + // setup tmp dir + const tmpDir = path.join(localDir, ".tmp"); - // VALIDATE COMMIT ORDER - // list all commits in order - // validate that a level number doesn't come before another level - // validate that a step falls within a level - // validate that steps are in order + try { + if (!(await exists(tmpDir))) { + await mkdir(tmpDir); + } + const tempGit: SimpleGit = gitP(tmpDir); + await tempGit.init(); - // on error, show level/step out of order + // VALIDATE TUTORIAL TESTS - // VALIDATE TUTORIAL TESTS - // load INIT commit(s) - // run test runner setup command(s) - // loop over commits: - // - load level commit - // - run level setup command(s) - // - load step setup commit(s) - // - run step setup command(s) - // - if next solution: - // - run test - expect fail - // - if solution - // - run test - expect pass + // run test runner setup command(s) + // loop over commits: + // - load level commit + // - run level setup command(s) + // - load step setup commit(s) + // - run step setup command(s) + // - if next solution: + // - run test - expect fail + // - if solution + // - run test - expect pass - // log level/step - // on error, show level/step & error message + // log level/step + // on error, show level/step & error message - // CLEANUP + // load INIT commit(s) + } catch (e) { + console.error(e.message); + } finally { + // cleanup + await rmdir(tmpDir); + } } export default validate; diff --git a/tests/validate.test.ts b/tests/tutorial.test.ts similarity index 100% rename from tests/validate.test.ts rename to tests/tutorial.test.ts From 40a57fb181a7735af3858f041c4d027814f7d987 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 08:31:25 -0700 Subject: [PATCH 02/11] switch to fs-extra in validate Signed-off-by: shmck --- package-lock.json | 44 +++++++++++++++++++++++++++++++++++++++++--- package.json | 2 ++ src/validate.ts | 22 +++++++++------------- 3 files changed, 52 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5c96108..ea709e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@coderoad/cli", - "version": "0.2.0", + "version": "0.2.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -960,6 +960,15 @@ "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", "dev": true }, + "@types/fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-B42Sxuaz09MhC3DDeW5kubRcQ5by4iuVQ0cRRWM2lggLzAa/KVom0Aft/208NgMvNQQZ86s5rVcqDdn/SH0/mg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/glob": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", @@ -1269,6 +1278,11 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "atob": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", @@ -2360,6 +2374,17 @@ "map-cache": "^0.2.2" } }, + "fs-extra": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -2463,8 +2488,7 @@ "graceful-fs": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "growly": { "version": "1.3.0", @@ -4141,6 +4165,15 @@ "minimist": "^1.2.5" } }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5832,6 +5865,11 @@ "set-value": "^2.0.1" } }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + }, "unset-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", diff --git a/package.json b/package.json index 5ac564c..6a9fee1 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "dependencies": { "ajv": "^6.12.2", "esm": "^3.2.25", + "fs-extra": "^9.0.1", "js-yaml": "^3.14.0", "kleur": "^3.0.3", "lodash": "^4.17.15", @@ -58,6 +59,7 @@ "devDependencies": { "@babel/preset-typescript": "^7.10.1", "@types/ajv": "^1.0.0", + "@types/fs-extra": "^9.0.1", "@types/inquirer": "^6.5.0", "@types/jest": "^25.2.3", "@types/js-yaml": "^3.12.4", diff --git a/src/validate.ts b/src/validate.ts index 4075640..8335da5 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -1,16 +1,9 @@ import * as path from "path"; -import * as fs from "fs"; -import util from "util"; +import * as fs from "fs-extra"; import * as yamlParser from "js-yaml"; import { getArg } from "./utils/args"; import gitP, { SimpleGit } from "simple-git/promise"; import { getCommits, CommitLogObject } from "./utils/commits"; -import simplegit from "simple-git/promise"; - -const mkdir = util.promisify(fs.mkdir); -const exists = util.promisify(fs.exists); -const rmdir = util.promisify(fs.rmdir); -const read = util.promisify(fs.readFile); async function validate(args: string[]) { // dir - default . @@ -22,7 +15,7 @@ async function validate(args: string[]) { yaml: getArg(args, { name: "yaml", alias: "y" }) || "coderoad.yaml", }; - const _yaml = await read(path.join(localDir, options.yaml), "utf8"); + const _yaml = await fs.readFile(path.join(localDir, options.yaml), "utf8"); // parse yaml config let skeleton; @@ -42,19 +35,22 @@ async function validate(args: string[]) { const codeBranch: string = skeleton.config.repo.branch; // validate commits - const commits = await getCommits({ localDir, codeBranch }); + const commits: CommitLogObject = await getCommits({ localDir, codeBranch }); + console.log("commits", commits); // setup tmp dir const tmpDir = path.join(localDir, ".tmp"); try { - if (!(await exists(tmpDir))) { - await mkdir(tmpDir); + if (!(await fs.pathExists(tmpDir))) { + await fs.emptyDir(tmpDir); } const tempGit: SimpleGit = gitP(tmpDir); await tempGit.init(); // VALIDATE TUTORIAL TESTS + if (commits.INIT) { + } // run test runner setup command(s) // loop over commits: @@ -75,7 +71,7 @@ async function validate(args: string[]) { console.error(e.message); } finally { // cleanup - await rmdir(tmpDir); + await fs.emptyDir(tmpDir); } } From 55c4dcbaedd562bcf6f6979118424c71c6e990bd Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 08:53:24 -0700 Subject: [PATCH 03/11] cherry-pick init commits Signed-off-by: shmck --- src/utils/cherryPick.ts | 29 +++++++++++++++++++++++++++++ src/validate.ts | 11 ++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/utils/cherryPick.ts diff --git a/src/utils/cherryPick.ts b/src/utils/cherryPick.ts new file mode 100644 index 0000000..bca5942 --- /dev/null +++ b/src/utils/cherryPick.ts @@ -0,0 +1,29 @@ +import { exec as cpExec } from "child_process"; +import { promisify } from "util"; + +const asyncExec = promisify(cpExec); + +export const exec = ( + command: string, + cwd: string +): Promise<{ stdout: string; stderr: string }> | never => { + return asyncExec(command, { cwd }); +}; + +export function gitPCherryPick(cwd: string) { + return async function cherryPick(commits: string[]): Promise { + for (const commit of commits) { + try { + const { stdout } = await exec( + `git cherry-pick -X theirs ${commit}`, + cwd + ); + if (!stdout) { + console.warn(`No cherry-pick output for ${commit}`); + } + } catch (e) { + console.warn(`Cherry-pick failed for ${commit}`); + } + } + }; +} diff --git a/src/validate.ts b/src/validate.ts index 8335da5..e74a460 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -3,6 +3,7 @@ import * as fs from "fs-extra"; import * as yamlParser from "js-yaml"; import { getArg } from "./utils/args"; import gitP, { SimpleGit } from "simple-git/promise"; +import { gitPCherryPick } from "./utils/cherryPick"; import { getCommits, CommitLogObject } from "./utils/commits"; async function validate(args: string[]) { @@ -46,10 +47,18 @@ async function validate(args: string[]) { await fs.emptyDir(tmpDir); } const tempGit: SimpleGit = gitP(tmpDir); + + console.log(Object.keys(gitP)); + await tempGit.init(); + await tempGit.addRemote("origin", skeleton.config.repo.uri); + await tempGit.fetch("origin", skeleton.config.repo.branch); + // no js cherry pick implementation + const cherryPick = gitPCherryPick(tmpDir); // VALIDATE TUTORIAL TESTS if (commits.INIT) { + cherryPick(commits.INIT); } // run test runner setup command(s) @@ -71,7 +80,7 @@ async function validate(args: string[]) { console.error(e.message); } finally { // cleanup - await fs.emptyDir(tmpDir); + // await fs.emptyDir(tmpDir); } } From b7acb6702c461589a9486d079ae6969581309544 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 09:04:38 -0700 Subject: [PATCH 04/11] run setup commands Signed-off-by: shmck --- src/utils/cherryPick.ts | 29 --------------------------- src/utils/exec.ts | 43 +++++++++++++++++++++++++++++++++++++++++ src/validate.ts | 17 +++++++++------- 3 files changed, 53 insertions(+), 36 deletions(-) delete mode 100644 src/utils/cherryPick.ts create mode 100644 src/utils/exec.ts diff --git a/src/utils/cherryPick.ts b/src/utils/cherryPick.ts deleted file mode 100644 index bca5942..0000000 --- a/src/utils/cherryPick.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { exec as cpExec } from "child_process"; -import { promisify } from "util"; - -const asyncExec = promisify(cpExec); - -export const exec = ( - command: string, - cwd: string -): Promise<{ stdout: string; stderr: string }> | never => { - return asyncExec(command, { cwd }); -}; - -export function gitPCherryPick(cwd: string) { - return async function cherryPick(commits: string[]): Promise { - for (const commit of commits) { - try { - const { stdout } = await exec( - `git cherry-pick -X theirs ${commit}`, - cwd - ); - if (!stdout) { - console.warn(`No cherry-pick output for ${commit}`); - } - } catch (e) { - console.warn(`Cherry-pick failed for ${commit}`); - } - } - }; -} diff --git a/src/utils/exec.ts b/src/utils/exec.ts new file mode 100644 index 0000000..04f0eb0 --- /dev/null +++ b/src/utils/exec.ts @@ -0,0 +1,43 @@ +import { exec as cpExec } from "child_process"; +import { promisify } from "util"; + +const asyncExec = promisify(cpExec); + +export function createExec(cwd: string) { + return function exec( + command: string + ): Promise<{ stdout: string; stderr: string }> | never { + return asyncExec(command, { cwd }); + }; +} + +export function createCherryPick(cwd: string) { + return async function cherryPick(commits: string[]): Promise { + for (const commit of commits) { + try { + const { stdout } = await createExec(cwd)( + `git cherry-pick -X theirs ${commit}` + ); + if (!stdout) { + console.warn(`No cherry-pick output for ${commit}`); + } + } catch (e) { + console.warn(`Cherry-pick failed for ${commit}`); + } + } + }; +} + +export function createCommandRunner(cwd: string) { + return async function runCommands(commands: string[]) { + for (const command of commands) { + try { + console.log(`> ${command}`); + await createExec(cwd)(command); + } catch (e) { + console.log(`Setup command failed: "${command}"`); + console.log(e.message); + } + } + }; +} diff --git a/src/validate.ts b/src/validate.ts index e74a460..32da7c1 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -3,7 +3,7 @@ import * as fs from "fs-extra"; import * as yamlParser from "js-yaml"; import { getArg } from "./utils/args"; import gitP, { SimpleGit } from "simple-git/promise"; -import { gitPCherryPick } from "./utils/cherryPick"; +import { createCommandRunner, createCherryPick } from "./utils/exec"; import { getCommits, CommitLogObject } from "./utils/commits"; async function validate(args: string[]) { @@ -23,8 +23,6 @@ async function validate(args: string[]) { try { skeleton = yamlParser.load(_yaml); - console.log("config", skeleton); - // TODO: validate yaml if (!skeleton) { throw new Error("Invalid yaml file contents"); } @@ -37,7 +35,6 @@ async function validate(args: string[]) { // validate commits const commits: CommitLogObject = await getCommits({ localDir, codeBranch }); - console.log("commits", commits); // setup tmp dir const tmpDir = path.join(localDir, ".tmp"); @@ -48,17 +45,23 @@ async function validate(args: string[]) { } const tempGit: SimpleGit = gitP(tmpDir); - console.log(Object.keys(gitP)); - await tempGit.init(); await tempGit.addRemote("origin", skeleton.config.repo.uri); await tempGit.fetch("origin", skeleton.config.repo.branch); // no js cherry pick implementation - const cherryPick = gitPCherryPick(tmpDir); + const cherryPick = createCherryPick(tmpDir); + const runCommands = createCommandRunner(tmpDir); // VALIDATE TUTORIAL TESTS if (commits.INIT) { + // load commits + console.info("Loading setup commits..."); cherryPick(commits.INIT); + + // run commands + if (skeleton.config?.testRunner?.setup?.commands) { + runCommands(skeleton.config?.testRunner?.setup?.commands); + } } // run test runner setup command(s) From 37b3d50ef550532416965e1cd5ec7beec64ea4d3 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 09:12:21 -0700 Subject: [PATCH 05/11] load step commits/commands Signed-off-by: shmck --- src/validate.ts | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/validate.ts b/src/validate.ts index 32da7c1..4c75e30 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -53,14 +53,48 @@ async function validate(args: string[]) { const runCommands = createCommandRunner(tmpDir); // VALIDATE TUTORIAL TESTS + + // setup if (commits.INIT) { // load commits console.info("Loading setup commits..."); - cherryPick(commits.INIT); + await cherryPick(commits.INIT); // run commands if (skeleton.config?.testRunner?.setup?.commands) { - runCommands(skeleton.config?.testRunner?.setup?.commands); + console.info("Running setup commands..."); + await runCommands(skeleton.config?.testRunner?.setup?.commands); + } + } + + console.log(skeleton.levels); + for (const level of skeleton.levels) { + if (level.setup) { + // load commits + if (level.setup.commits) { + console.log(`Loading ${level.id} commits...`); + await cherryPick(commits[level.id]); + } + // run commands + if (level.setup.commands) { + console.log(`Running ${level.id} commands...`); + await runCommands(level.setup.commands); + } + } + // steps + if (level.steps) { + for (const step of level.steps) { + // load commits + if (step.setup.commits) { + console.log(`Loading ${step.id} commits...`); + await cherryPick(commits[step.id]); + } + // run commands + if (step.setup.commands) { + console.log(`Running ${step.id} commands...`); + await runCommands(step.setup.commands); + } + } } } From 9df45b102a06a5d8f52d8d1aee3af34df577bf4f Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 09:34:02 -0700 Subject: [PATCH 06/11] test runner progress Signed-off-by: shmck --- src/utils/exec.ts | 37 +++++++++++++++++++++++++++++++++++-- src/validate.ts | 20 +++++++++++++++++--- typings/tutorial.d.ts | 2 +- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/utils/exec.ts b/src/utils/exec.ts index 04f0eb0..57d7141 100644 --- a/src/utils/exec.ts +++ b/src/utils/exec.ts @@ -1,4 +1,6 @@ +import * as T from "../../typings/tutorial"; import { exec as cpExec } from "child_process"; +import * as path from "path"; import { promisify } from "util"; const asyncExec = promisify(cpExec); @@ -29,11 +31,15 @@ export function createCherryPick(cwd: string) { } export function createCommandRunner(cwd: string) { - return async function runCommands(commands: string[]) { + return async function runCommands(commands: string[], dir?: string) { for (const command of commands) { try { console.log(`> ${command}`); - await createExec(cwd)(command); + let cwdDir = cwd; + if (dir) { + cwdDir = path.join(cwd, dir); + } + await createExec(cwdDir)(command); } catch (e) { console.log(`Setup command failed: "${command}"`); console.log(e.message); @@ -41,3 +47,30 @@ export function createCommandRunner(cwd: string) { } }; } + +function isAbsolute(p: string) { + return path.normalize(p + "/") === path.normalize(path.resolve(p) + "/"); +} + +export function createTestRunner(cwd: string, config: T.TestRunnerConfig) { + const { command, args, directory } = config; + + const commandIsAbsolute = isAbsolute(command); + + let runnerPath; + if (commandIsAbsolute) { + // absolute path + runnerPath = command; + } else { + // relative path + runnerPath = path.join(cwd, directory || "", command); + } + + const commandWithArgs = `${runnerPath} ${args.tap}`; + + return async function runTest() { + const { stdout, stderr } = await createExec(cwd)(commandWithArgs); + console.log(stdout); + console.warn(stderr); + }; +} diff --git a/src/validate.ts b/src/validate.ts index 4c75e30..a6ef57a 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -3,7 +3,11 @@ import * as fs from "fs-extra"; import * as yamlParser from "js-yaml"; import { getArg } from "./utils/args"; import gitP, { SimpleGit } from "simple-git/promise"; -import { createCommandRunner, createCherryPick } from "./utils/exec"; +import { + createCommandRunner, + createCherryPick, + createTestRunner, +} from "./utils/exec"; import { getCommits, CommitLogObject } from "./utils/commits"; async function validate(args: string[]) { @@ -51,6 +55,7 @@ async function validate(args: string[]) { // no js cherry pick implementation const cherryPick = createCherryPick(tmpDir); const runCommands = createCommandRunner(tmpDir); + const runTest = createTestRunner(tmpDir, skeleton.config.testRunner); // VALIDATE TUTORIAL TESTS @@ -63,11 +68,15 @@ async function validate(args: string[]) { // run commands if (skeleton.config?.testRunner?.setup?.commands) { console.info("Running setup commands..."); - await runCommands(skeleton.config?.testRunner?.setup?.commands); + + await runCommands( + skeleton.config?.testRunner?.setup?.commands, + // add optional setup directory + skeleton.config?.testRunner?.directory + ); } } - console.log(skeleton.levels); for (const level of skeleton.levels) { if (level.setup) { // load commits @@ -84,6 +93,7 @@ async function validate(args: string[]) { // steps if (level.steps) { for (const step of level.steps) { + console.log(step); // load commits if (step.setup.commits) { console.log(`Loading ${step.id} commits...`); @@ -94,6 +104,10 @@ async function validate(args: string[]) { console.log(`Running ${step.id} commands...`); await runCommands(step.setup.commands); } + + // run test + console.info("Running test"); + await runTest(); } } } diff --git a/typings/tutorial.d.ts b/typings/tutorial.d.ts index f3fe401..82b1e2e 100644 --- a/typings/tutorial.d.ts +++ b/typings/tutorial.d.ts @@ -62,7 +62,7 @@ export interface TestRunnerArgs { export interface TestRunnerConfig { command: string; - args?: TestRunnerArgs; + args: TestRunnerArgs; directory?: string; setup?: StepActions; } From 6f1c7fffe7431f8e3855ae62cbcd99546c1b6c57 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 09:47:57 -0700 Subject: [PATCH 07/11] load solution commits Signed-off-by: shmck --- src/validate.ts | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/validate.ts b/src/validate.ts index a6ef57a..7d8bf48 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -80,7 +80,7 @@ async function validate(args: string[]) { for (const level of skeleton.levels) { if (level.setup) { // load commits - if (level.setup.commits) { + if (commits[`${level.id}`]) { console.log(`Loading ${level.id} commits...`); await cherryPick(commits[level.id]); } @@ -93,21 +93,36 @@ async function validate(args: string[]) { // steps if (level.steps) { for (const step of level.steps) { - console.log(step); // load commits - if (step.setup.commits) { - console.log(`Loading ${step.id} commits...`); - await cherryPick(commits[step.id]); + const stepSetupCommits = commits[`${step.id}Q`]; + if (stepSetupCommits) { + console.info(`Loading ${step.id} setup commits...`); + await cherryPick(stepSetupCommits); } // run commands if (step.setup.commands) { - console.log(`Running ${step.id} commands...`); + console.info(`Running ${step.id} setup commands...`); await runCommands(step.setup.commands); } - // run test - console.info("Running test"); - await runTest(); + // ignore runnning tests on steps with no solution + if (step.solution) { + // run test + console.info("Running test"); + // await runTest(); + } + + const stepSolutionCommits = commits[`${step.id}A`]; + if (stepSolutionCommits) { + console.info(`Loading ${step.id} solution commits...`); + await cherryPick(stepSolutionCommits); + } + + // run commands + if (step?.solution?.commands) { + console.info(`Running ${step.id} solution commands...`); + await runCommands(step.solution.commands); + } } } } From 514b2ad5b781e52b83944618ee1e7614344f8d88 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 09:53:39 -0700 Subject: [PATCH 08/11] prepare for test runner tests Signed-off-by: shmck --- src/validate.ts | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/validate.ts b/src/validate.ts index 7d8bf48..32b6946 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -78,14 +78,14 @@ async function validate(args: string[]) { } for (const level of skeleton.levels) { - if (level.setup) { + if (level?.setup) { // load commits if (commits[`${level.id}`]) { console.log(`Loading ${level.id} commits...`); await cherryPick(commits[level.id]); } // run commands - if (level.setup.commands) { + if (level.setup?.commands) { console.log(`Running ${level.id} commands...`); await runCommands(level.setup.commands); } @@ -105,14 +105,17 @@ async function validate(args: string[]) { await runCommands(step.setup.commands); } - // ignore runnning tests on steps with no solution - if (step.solution) { + const stepSolutionCommits = commits[`${step.id}A`]; + const hasSolution = step.solution || stepSolutionCommits; + + // ignore running tests on steps with no solution + if (hasSolution) { // run test - console.info("Running test"); + console.info("Running setup test"); + // expect fail // await runTest(); } - const stepSolutionCommits = commits[`${step.id}A`]; if (stepSolutionCommits) { console.info(`Loading ${step.id} solution commits...`); await cherryPick(stepSolutionCommits); @@ -123,30 +126,24 @@ async function validate(args: string[]) { console.info(`Running ${step.id} solution commands...`); await runCommands(step.solution.commands); } + + if (hasSolution) { + // run test + console.info("Running solution test"); + // expect pass + // await runTest(); + } } } } - // run test runner setup command(s) - // loop over commits: - // - load level commit - // - run level setup command(s) - // - load step setup commit(s) - // - run step setup command(s) - // - if next solution: - // - run test - expect fail - // - if solution - // - run test - expect pass - // log level/step // on error, show level/step & error message - - // load INIT commit(s) } catch (e) { console.error(e.message); } finally { // cleanup - // await fs.emptyDir(tmpDir); + await fs.emptyDir(tmpDir); } } From 16e42f125505545830b2806532a3262d063ce1eb Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 11:54:08 -0700 Subject: [PATCH 09/11] working solution test runner Signed-off-by: shmck --- src/utils/exec.ts | 62 +++++++++++++++++++++++++++++------------------ src/validate.ts | 4 +-- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/utils/exec.ts b/src/utils/exec.ts index 57d7141..a03d2cb 100644 --- a/src/utils/exec.ts +++ b/src/utils/exec.ts @@ -6,10 +6,15 @@ import { promisify } from "util"; const asyncExec = promisify(cpExec); export function createExec(cwd: string) { - return function exec( + return async function exec( command: string - ): Promise<{ stdout: string; stderr: string }> | never { - return asyncExec(command, { cwd }); + ): Promise<{ stdout: string | null; stderr: string }> { + try { + const result = await asyncExec(command, { cwd }); + return result; + } catch (e) { + return { stdout: null, stderr: e.message }; + } }; } @@ -31,7 +36,11 @@ export function createCherryPick(cwd: string) { } export function createCommandRunner(cwd: string) { - return async function runCommands(commands: string[], dir?: string) { + return async function runCommands( + commands: string[], + dir?: string + ): Promise { + let errors = []; for (const command of commands) { try { console.log(`> ${command}`); @@ -39,38 +48,45 @@ export function createCommandRunner(cwd: string) { if (dir) { cwdDir = path.join(cwd, dir); } - await createExec(cwdDir)(command); + const { stdout, stderr } = await createExec(cwdDir)(command); + + console.warn(stderr); + console.log(stdout); } catch (e) { - console.log(`Setup command failed: "${command}"`); - console.log(e.message); + console.error(`Command failed: "${command}"`); + console.warn(e.message); + errors.push(e.message); } } + return !!errors.length; }; } -function isAbsolute(p: string) { - return path.normalize(p + "/") === path.normalize(path.resolve(p) + "/"); -} +// function isAbsolute(p: string) { +// return path.normalize(p + "/") === path.normalize(path.resolve(p) + "/"); +// } export function createTestRunner(cwd: string, config: T.TestRunnerConfig) { const { command, args, directory } = config; - const commandIsAbsolute = isAbsolute(command); + // const commandIsAbsolute = isAbsolute(command); - let runnerPath; - if (commandIsAbsolute) { - // absolute path - runnerPath = command; - } else { - // relative path - runnerPath = path.join(cwd, directory || "", command); + let wd = cwd; + if (directory) { + wd = path.join(cwd, directory); } - const commandWithArgs = `${runnerPath} ${args.tap}`; + const commandWithArgs = `${command} ${args.tap}`; - return async function runTest() { - const { stdout, stderr } = await createExec(cwd)(commandWithArgs); - console.log(stdout); - console.warn(stderr); + return async function runTest(): Promise<{ + stdout: string | null; + stderr: string | null; + }> { + try { + // console.log(await createExec(wd)("ls -a node_modules/.bin")); + return await createExec(wd)(commandWithArgs); + } catch (e) { + return Promise.resolve({ stdout: null, stderr: e.message }); + } }; } diff --git a/src/validate.ts b/src/validate.ts index 32b6946..f9fe146 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -57,8 +57,6 @@ async function validate(args: string[]) { const runCommands = createCommandRunner(tmpDir); const runTest = createTestRunner(tmpDir, skeleton.config.testRunner); - // VALIDATE TUTORIAL TESTS - // setup if (commits.INIT) { // load commits @@ -131,7 +129,7 @@ async function validate(args: string[]) { // run test console.info("Running solution test"); // expect pass - // await runTest(); + await runTest(); } } } From abd46f1a1231f11eb4832059f85f973608d16451 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 11:57:52 -0700 Subject: [PATCH 10/11] add setup tests Signed-off-by: shmck --- src/validate.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/validate.ts b/src/validate.ts index f9fe146..b3a1f3f 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -111,7 +111,13 @@ async function validate(args: string[]) { // run test console.info("Running setup test"); // expect fail - // await runTest(); + const { stdout, stderr } = await runTest(); + if (stdout) { + console.error( + `Expected ${step.id} setup tests to fail, but passed` + ); + console.log(stdout); + } } if (stepSolutionCommits) { @@ -129,7 +135,13 @@ async function validate(args: string[]) { // run test console.info("Running solution test"); // expect pass - await runTest(); + const { stdout, stderr } = await runTest(); + if (stderr) { + console.error( + `Expected ${step.id} solution tests to pass, but failed` + ); + console.log(stderr); + } } } } From 2fc111324fcdd12037012723f95733cd9aa357e0 Mon Sep 17 00:00:00 2001 From: shmck Date: Sat, 13 Jun 2020 12:08:05 -0700 Subject: [PATCH 11/11] cleanup output text ui Signed-off-by: shmck --- src/utils/exec.ts | 3 +-- src/validate.ts | 33 +++++++++++++++++++-------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/utils/exec.ts b/src/utils/exec.ts index a03d2cb..a209b2d 100644 --- a/src/utils/exec.ts +++ b/src/utils/exec.ts @@ -43,7 +43,7 @@ export function createCommandRunner(cwd: string) { let errors = []; for (const command of commands) { try { - console.log(`> ${command}`); + console.log(`--> ${command}`); let cwdDir = cwd; if (dir) { cwdDir = path.join(cwd, dir); @@ -51,7 +51,6 @@ export function createCommandRunner(cwd: string) { const { stdout, stderr } = await createExec(cwdDir)(command); console.warn(stderr); - console.log(stdout); } catch (e) { console.error(`Command failed: "${command}"`); console.warn(e.message); diff --git a/src/validate.ts b/src/validate.ts index b3a1f3f..e5ed11e 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -58,14 +58,15 @@ async function validate(args: string[]) { const runTest = createTestRunner(tmpDir, skeleton.config.testRunner); // setup + console.info("* Setup"); if (commits.INIT) { // load commits - console.info("Loading setup commits..."); + console.info("-- Loading commits..."); await cherryPick(commits.INIT); // run commands if (skeleton.config?.testRunner?.setup?.commands) { - console.info("Running setup commands..."); + console.info("-- Running commands..."); await runCommands( skeleton.config?.testRunner?.setup?.commands, @@ -76,30 +77,32 @@ async function validate(args: string[]) { } for (const level of skeleton.levels) { + console.info(`* ${level.id}`); if (level?.setup) { // load commits if (commits[`${level.id}`]) { - console.log(`Loading ${level.id} commits...`); + console.log(`-- Loading commits...`); await cherryPick(commits[level.id]); } // run commands if (level.setup?.commands) { - console.log(`Running ${level.id} commands...`); + console.log(`-- Running commands...`); await runCommands(level.setup.commands); } } // steps if (level.steps) { for (const step of level.steps) { + console.info(`** ${step.id}`); // load commits const stepSetupCommits = commits[`${step.id}Q`]; if (stepSetupCommits) { - console.info(`Loading ${step.id} setup commits...`); + console.info(`--- Loading setup commits...`); await cherryPick(stepSetupCommits); } // run commands if (step.setup.commands) { - console.info(`Running ${step.id} setup commands...`); + console.info(`--- Running setup commands...`); await runCommands(step.setup.commands); } @@ -109,37 +112,39 @@ async function validate(args: string[]) { // ignore running tests on steps with no solution if (hasSolution) { // run test - console.info("Running setup test"); + console.info("--- Running setup test..."); // expect fail const { stdout, stderr } = await runTest(); if (stdout) { console.error( - `Expected ${step.id} setup tests to fail, but passed` + `--- Expected ${step.id} setup tests to fail, but passed` ); + // log tests console.log(stdout); } } if (stepSolutionCommits) { - console.info(`Loading ${step.id} solution commits...`); + console.info(`--- Loading solution commits...`); await cherryPick(stepSolutionCommits); } // run commands if (step?.solution?.commands) { - console.info(`Running ${step.id} solution commands...`); + console.info(`--- Running solution commands...`); await runCommands(step.solution.commands); } if (hasSolution) { // run test - console.info("Running solution test"); + console.info("--- Running solution test..."); // expect pass const { stdout, stderr } = await runTest(); if (stderr) { console.error( - `Expected ${step.id} solution tests to pass, but failed` + `--- Expected ${step.id} solution tests to pass, but failed` ); + // log tests console.log(stderr); } } @@ -147,9 +152,9 @@ async function validate(args: string[]) { } } - // log level/step - // on error, show level/step & error message + console.info(`\n✔ Success!`); } catch (e) { + console.error("\n✘ Fail!"); console.error(e.message); } finally { // cleanup