From c55c67e0a5674194b2dd5cee1223d97bb603d87c Mon Sep 17 00:00:00 2001 From: Divlo Date: Fri, 25 Jun 2021 12:46:01 +0200 Subject: [PATCH] feat(cli): add --ci option to `run test` command --- .github/workflows/ci.yml | 2 +- cli/commands/run/test.ts | 9 +++++- cli/services/GitAffected.ts | 64 +++++++++++++++++++++++++++++++------ package-lock.json | 55 ++++--------------------------- package.json | 1 - 5 files changed, 69 insertions(+), 62 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 220b74e..4a6326d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,4 +108,4 @@ jobs: run: 'npm install --global' - name: 'Test' - run: 'programming-challenges run test --affected' + run: 'programming-challenges run test --affected --ci' diff --git a/cli/commands/run/test.ts b/cli/commands/run/test.ts index fcf8c83..e73337e 100644 --- a/cli/commands/run/test.ts +++ b/cli/commands/run/test.ts @@ -4,7 +4,7 @@ import * as typanion from 'typanion' import chalk from 'chalk' import { Solution } from '../../services/Solution' -import { gitAffected } from '../../services/GitAffected' +import { GitAffected } from '../../services/GitAffected' const successMessage = `${chalk.bold.green('Success:')} Tests passed! 🎉` @@ -35,10 +35,17 @@ export class RunTestCommand extends Command { description: 'Only run the tests for the affected files in `git`.' }) + public isContinuousIntegration = Option.Boolean('--ci', false, { + description: 'Run the tests for the Continuous Integration (CI).' + }) + async execute (): Promise { console.log() try { if (this.affected) { + const gitAffected = new GitAffected({ + isContinuousIntegration: this.isContinuousIntegration + }) const solutions = await gitAffected.getAffectedSolutions() for (const solution of solutions) { await solution.test() diff --git a/cli/services/GitAffected.ts b/cli/services/GitAffected.ts index 533914b..3eb7bfc 100644 --- a/cli/services/GitAffected.ts +++ b/cli/services/GitAffected.ts @@ -1,20 +1,66 @@ -import simpleGit from 'simple-git' +import execa from 'execa' import { Challenge } from './Challenge' import { Solution } from './Solution' -const git = simpleGit() - const solutionsRegex = new RegExp( /challenges\/[\s\S]*\/solutions\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/[\s\S]*\/(solution|Solution).(c|cpp|cs|dart|java|js|py|rs|ts)/ ) -class GitAffected { +export interface GitAffectedOptions { + isContinuousIntegration: boolean +} + +export class GitAffected implements GitAffectedOptions { + public isContinuousIntegration: boolean + + constructor (options: GitAffectedOptions) { + this.isContinuousIntegration = options.isContinuousIntegration + } + + public parseGitOutput (output: string): string[] { + return output + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0) + } + + public async getFilesUsingBaseAndHead ( + base: string, + head: string + ): Promise { + const { stdout } = await execa.command( + `git diff --name-only --relative ${base} ${head}` + ) + return this.parseGitOutput(stdout) + } + + public async getUncommittedFiles (): Promise { + return await this.getFilesUsingBaseAndHead('HEAD', '.') + } + + public async getLatestPushedCommit (): Promise { + const latestCommit = this.isContinuousIntegration ? '~1' : '' + const { stdout } = await execa.command(`git rev-parse origin/master${latestCommit}`) + return stdout + } + + public async getUnpushedFiles (): Promise { + return await this.getFilesUsingBaseAndHead( + await this.getLatestPushedCommit(), + '.' + ) + } + public async getAffectedSolutions (): Promise { - await git.add('.') - const diff = await git.diff(['--name-only', '--staged']) - const affectedSolutionsPaths = diff.split('\n').filter((currentDiff) => { - return solutionsRegex.test(currentDiff) + const files = Array.from( + new Set([ + ...(await this.getUnpushedFiles()), + ...(await this.getUncommittedFiles()) + ]) + ) + const affectedSolutionsPaths = files.filter((filePath) => { + return solutionsRegex.test(filePath) }) return affectedSolutionsPaths.map((solution) => { const [, challengeName, , programmingLanguageName, solutionName] = @@ -29,5 +75,3 @@ class GitAffected { }) } } - -export const gitAffected = new GitAffected() diff --git a/package-lock.json b/package-lock.json index 1384f1d..59b54bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,6 @@ "execa": "5.1.1", "ora": "5.4.0", "replace-in-file": "6.2.0", - "simple-git": "2.39.1", "table": "6.7.1", "typanion": "3.3.1", "validate-npm-package-name": "3.0.0" @@ -1190,19 +1189,6 @@ "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "dependencies": { - "debug": "^4.1.1" - } - }, - "node_modules/@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2445,6 +2431,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -6356,7 +6343,8 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "node_modules/natural-compare": { "version": "1.4.0", @@ -7527,16 +7515,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, - "node_modules/simple-git": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.39.1.tgz", - "integrity": "sha512-+kEAkyQHsWejYxQNCzTrjvCxJOcijpB49RSs7HV+TK9B9prUq7YBNpFstQvjfGBn4Hecywp4tm+breGGGHlHwA==", - "dependencies": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.3.1" - } - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -9473,19 +9451,6 @@ "chalk": "^4.0.0" } }, - "@kwsites/file-exists": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", - "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", - "requires": { - "debug": "^4.1.1" - } - }, - "@kwsites/promise-deferred": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", - "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" - }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10420,6 +10385,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, "requires": { "ms": "2.1.2" } @@ -13314,7 +13280,8 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true }, "natural-compare": { "version": "1.4.0", @@ -14163,16 +14130,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" }, - "simple-git": { - "version": "2.39.1", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.39.1.tgz", - "integrity": "sha512-+kEAkyQHsWejYxQNCzTrjvCxJOcijpB49RSs7HV+TK9B9prUq7YBNpFstQvjfGBn4Hecywp4tm+breGGGHlHwA==", - "requires": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.3.1" - } - }, "sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/package.json b/package.json index 0e4cea2..60d0d46 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "execa": "5.1.1", "ora": "5.4.0", "replace-in-file": "6.2.0", - "simple-git": "2.39.1", "table": "6.7.1", "typanion": "3.3.1", "validate-npm-package-name": "3.0.0"