mirror of
https://github.com/theoludwig/programming-challenges.git
synced 2024-12-08 00:45:29 +01:00
fix(cli): changes to templates/docker
, should run all affected tests
This commit is contained in:
parent
9621751a4d
commit
17efe8a113
@ -1,6 +1,3 @@
|
|||||||
import fs from 'node:fs'
|
|
||||||
import path from 'node:path'
|
|
||||||
|
|
||||||
import { Command, Option } from 'clipanion'
|
import { Command, Option } from 'clipanion'
|
||||||
import * as typanion from 'typanion'
|
import * as typanion from 'typanion'
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
@ -8,8 +5,7 @@ import chalk from 'chalk'
|
|||||||
import { Solution } from '../../services/Solution'
|
import { Solution } from '../../services/Solution'
|
||||||
import { GitAffected } from '../../services/GitAffected'
|
import { GitAffected } from '../../services/GitAffected'
|
||||||
import { template } from '../../services/Template'
|
import { template } from '../../services/Template'
|
||||||
|
import { Test, successMessage } from '../../services/Test'
|
||||||
const successMessage = `${chalk.bold.green('Success:')} Tests passed! 🎉`
|
|
||||||
|
|
||||||
export class RunTestCommand extends Command {
|
export class RunTestCommand extends Command {
|
||||||
static paths = [['run', 'test']]
|
static paths = [['run', 'test']]
|
||||||
@ -50,15 +46,6 @@ export class RunTestCommand extends Command {
|
|||||||
description: 'Base of the current branch (usually master)'
|
description: 'Base of the current branch (usually master)'
|
||||||
})
|
})
|
||||||
|
|
||||||
async runTests (solutions: Solution[]): Promise<number> {
|
|
||||||
for (const solution of solutions) {
|
|
||||||
await solution.test()
|
|
||||||
console.log('\n------------------------------\n')
|
|
||||||
}
|
|
||||||
console.log(successMessage)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute (): Promise<number> {
|
async execute (): Promise<number> {
|
||||||
console.log()
|
console.log()
|
||||||
try {
|
try {
|
||||||
@ -66,35 +53,7 @@ export class RunTestCommand extends Command {
|
|||||||
await template.verifySupportedProgrammingLanguage(this.programmingLanguage)
|
await template.verifySupportedProgrammingLanguage(this.programmingLanguage)
|
||||||
}
|
}
|
||||||
if (this.all) {
|
if (this.all) {
|
||||||
const challengesPath = path.join(
|
return await Test.runAllTests(this.programmingLanguage)
|
||||||
__dirname,
|
|
||||||
'..',
|
|
||||||
'..',
|
|
||||||
'..',
|
|
||||||
'challenges'
|
|
||||||
)
|
|
||||||
const challenges = await fs.promises.readdir(challengesPath)
|
|
||||||
const paths: string[] = []
|
|
||||||
for (const challenge of challenges) {
|
|
||||||
const solutionsPath = path.join(challengesPath, challenge, 'solutions')
|
|
||||||
const languagesSolution = (await fs.promises.readdir(solutionsPath)).filter(
|
|
||||||
(name) => {
|
|
||||||
if (this.programmingLanguage != null) {
|
|
||||||
return name === this.programmingLanguage
|
|
||||||
}
|
|
||||||
return name !== '.gitkeep'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
for (const language of languagesSolution) {
|
|
||||||
const solutionPath = (await fs.promises.readdir(path.join(solutionsPath, language))).map((solutionName) => {
|
|
||||||
return `challenges/${challenge}/solutions/${language}/${solutionName}`
|
|
||||||
})
|
|
||||||
paths.push(...solutionPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const solutions = await Solution.getManyByPaths(paths)
|
|
||||||
await this.runTests(solutions)
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
if (this.affected) {
|
if (this.affected) {
|
||||||
const gitAffected = new GitAffected({
|
const gitAffected = new GitAffected({
|
||||||
@ -102,7 +61,7 @@ export class RunTestCommand extends Command {
|
|||||||
base: this.base
|
base: this.base
|
||||||
})
|
})
|
||||||
const solutions = await gitAffected.getAffectedSolutions()
|
const solutions = await gitAffected.getAffectedSolutions()
|
||||||
return await this.runTests(solutions)
|
return await Test.runManyWithSolutions(solutions)
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
this.solutionName == null ||
|
this.solutionName == null ||
|
||||||
|
@ -6,6 +6,10 @@ 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)/
|
/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)/
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const dockerRegex = new RegExp(
|
||||||
|
/templates\/docker\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/Dockerfile/
|
||||||
|
)
|
||||||
|
|
||||||
export interface GitAffectedOptions {
|
export interface GitAffectedOptions {
|
||||||
isContinuousIntegration: boolean
|
isContinuousIntegration: boolean
|
||||||
base?: string
|
base?: string
|
||||||
@ -70,6 +74,21 @@ export class GitAffected implements GitAffectedOptions {
|
|||||||
const affectedSolutionsPaths = files.filter((filePath) => {
|
const affectedSolutionsPaths = files.filter((filePath) => {
|
||||||
return solutionsRegex.test(filePath)
|
return solutionsRegex.test(filePath)
|
||||||
})
|
})
|
||||||
return await Solution.getManyByPaths(affectedSolutionsPaths)
|
const affectedDockerPaths = files.filter((filePath) => {
|
||||||
|
return dockerRegex.test(filePath)
|
||||||
|
})
|
||||||
|
const affectedLanguages = affectedDockerPaths.map((filePath) => {
|
||||||
|
const [,, programmingLanguageName] = filePath.replaceAll('\\', '/').split('/')
|
||||||
|
return programmingLanguageName
|
||||||
|
})
|
||||||
|
const solutionsChallenges = await Solution.getManyByPaths(affectedSolutionsPaths)
|
||||||
|
const solutionsDocker = await Solution.getManyByProgrammingLanguages(affectedLanguages)
|
||||||
|
const solutions: Solution[] = solutionsDocker
|
||||||
|
for (const solution of solutionsChallenges) {
|
||||||
|
if (!affectedLanguages.includes(solution.programmingLanguageName)) {
|
||||||
|
solutions.push(solution)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return solutions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
|
import fs from 'node:fs'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createTemporaryEmptyFolder,
|
createTemporaryEmptyFolder,
|
||||||
@ -102,6 +103,33 @@ export class Solution implements SolutionOptions {
|
|||||||
return solution
|
return solution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getManyByProgrammingLanguages (programmingLanguages?: string[]): Promise<Solution[]> {
|
||||||
|
const languages = programmingLanguages ?? await template.getProgrammingLanguages()
|
||||||
|
const challengesPath = path.join(
|
||||||
|
__dirname,
|
||||||
|
'..',
|
||||||
|
'..',
|
||||||
|
'challenges'
|
||||||
|
)
|
||||||
|
const challenges = await fs.promises.readdir(challengesPath)
|
||||||
|
const paths: string[] = []
|
||||||
|
for (const challenge of challenges) {
|
||||||
|
const solutionsPath = path.join(challengesPath, challenge, 'solutions')
|
||||||
|
const languagesSolution = (await fs.promises.readdir(solutionsPath)).filter(
|
||||||
|
(name) => {
|
||||||
|
return name !== '.gitkeep' && languages.includes(name)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
for (const language of languagesSolution) {
|
||||||
|
const solutionPath = (await fs.promises.readdir(path.join(solutionsPath, language))).map((solutionName) => {
|
||||||
|
return `challenges/${challenge}/solutions/${language}/${solutionName}`
|
||||||
|
})
|
||||||
|
paths.push(...solutionPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return await Solution.getManyByPaths(paths)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Solutions by relative paths.
|
* Get Solutions by relative paths.
|
||||||
* @param paths relative to `challenges` (e.g: `challenges/hello-world/solutions/c/function`)
|
* @param paths relative to `challenges` (e.g: `challenges/hello-world/solutions/c/function`)
|
||||||
|
@ -5,7 +5,6 @@ import { replaceInFile } from 'replace-in-file'
|
|||||||
import date from 'date-and-time'
|
import date from 'date-and-time'
|
||||||
|
|
||||||
import { copyDirectory } from '../utils/copyDirectory'
|
import { copyDirectory } from '../utils/copyDirectory'
|
||||||
import { isExistingPath } from '../utils/isExistingPath'
|
|
||||||
|
|
||||||
const TEMPLATE_PATH = path.join(__dirname, '..', '..', 'templates')
|
const TEMPLATE_PATH = path.join(__dirname, '..', '..', 'templates')
|
||||||
const TEMPLATE_DOCKER_PATH = path.join(TEMPLATE_PATH, 'docker')
|
const TEMPLATE_DOCKER_PATH = path.join(TEMPLATE_PATH, 'docker')
|
||||||
@ -94,9 +93,14 @@ class Template {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getProgrammingLanguages (): Promise<string[]> {
|
||||||
|
const languages = await fs.promises.readdir(TEMPLATE_SOLUTION_PATH)
|
||||||
|
return languages.filter(language => language !== 'base')
|
||||||
|
}
|
||||||
|
|
||||||
public async verifySupportedProgrammingLanguage (language: string): Promise<void> {
|
public async verifySupportedProgrammingLanguage (language: string): Promise<void> {
|
||||||
const templateLanguagePath = path.join(TEMPLATE_SOLUTION_PATH, language)
|
const languages = await this.getProgrammingLanguages()
|
||||||
if (!(await isExistingPath(templateLanguagePath))) {
|
if (!languages.includes(language)) {
|
||||||
throw new Error('This programming language is not supported yet.')
|
throw new Error('This programming language is not supported yet.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,8 @@ export interface TestOptions {
|
|||||||
elapsedTimeMilliseconds: number
|
elapsedTimeMilliseconds: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const successMessage = `${chalk.bold.green('Success:')} Tests passed! 🎉`
|
||||||
|
|
||||||
export class Test implements TestOptions {
|
export class Test implements TestOptions {
|
||||||
public index: number
|
public index: number
|
||||||
public path: string
|
public path: string
|
||||||
@ -132,6 +134,23 @@ export class Test implements TestOptions {
|
|||||||
return { input, output }
|
return { input, output }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async runManyWithSolutions (solutions: Solution[]): Promise<number> {
|
||||||
|
for (const solution of solutions) {
|
||||||
|
await solution.test()
|
||||||
|
console.log('\n------------------------------\n')
|
||||||
|
}
|
||||||
|
console.log(successMessage)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
static async runAllTests (programmingLanguage?: string): Promise<number> {
|
||||||
|
const solutions = await Solution.getManyByProgrammingLanguages(
|
||||||
|
programmingLanguage != null ? [programmingLanguage] : undefined
|
||||||
|
)
|
||||||
|
await Test.runManyWithSolutions(solutions)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
static async run (options: TestRunOptions): Promise<Test> {
|
static async run (options: TestRunOptions): Promise<Test> {
|
||||||
const { input, output } = await Test.getInputOutput(options.path)
|
const { input, output } = await Test.getInputOutput(options.path)
|
||||||
const start = performance.now()
|
const start = performance.now()
|
||||||
|
2301
package-lock.json
generated
2301
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@ -41,25 +41,25 @@
|
|||||||
"date-and-time": "2.0.1",
|
"date-and-time": "2.0.1",
|
||||||
"execa": "5.1.1",
|
"execa": "5.1.1",
|
||||||
"ora": "5.4.1",
|
"ora": "5.4.1",
|
||||||
"replace-in-file": "6.2.0",
|
"replace-in-file": "6.3.2",
|
||||||
"table": "6.7.2",
|
"table": "6.7.3",
|
||||||
"typanion": "3.5.0",
|
"typanion": "3.7.1",
|
||||||
"validate-npm-package-name": "3.0.0"
|
"validate-npm-package-name": "3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "13.2.1",
|
"@commitlint/cli": "14.1.0",
|
||||||
"@commitlint/config-conventional": "13.2.0",
|
"@commitlint/config-conventional": "14.1.0",
|
||||||
"@types/date-and-time": "0.13.0",
|
"@types/date-and-time": "0.13.0",
|
||||||
"@types/jest": "27.0.2",
|
"@types/jest": "27.0.2",
|
||||||
"@types/mock-fs": "4.13.1",
|
"@types/mock-fs": "4.13.1",
|
||||||
"@types/node": "16.10.8",
|
"@types/node": "16.11.7",
|
||||||
"@types/validate-npm-package-name": "3.0.3",
|
"@types/validate-npm-package-name": "3.0.3",
|
||||||
"editorconfig-checker": "4.0.2",
|
"editorconfig-checker": "4.0.2",
|
||||||
"jest": "27.2.5",
|
"jest": "27.3.1",
|
||||||
"markdownlint-cli": "0.29.0",
|
"markdownlint-cli": "0.29.0",
|
||||||
"mock-fs": "5.1.1",
|
"mock-fs": "5.1.2",
|
||||||
"rimraf": "3.0.2",
|
"rimraf": "3.0.2",
|
||||||
"ts-jest": "27.0.5",
|
"ts-jest": "27.0.7",
|
||||||
"ts-standard": "10.0.0",
|
"ts-standard": "10.0.0",
|
||||||
"typescript": "4.4.4"
|
"typescript": "4.4.4"
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
FROM dart:2.14.3
|
FROM dart:2.14.4
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
CMD ["dart", "run", "solution.dart"]
|
CMD ["dart", "run", "solution.dart"]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM rust:1.55.0
|
FROM rust:1.56.1
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
RUN rustc solution.rs
|
RUN rustc solution.rs
|
||||||
CMD ["./solution"]
|
CMD ["./solution"]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM node:16.11.1
|
FROM node:16.13.0
|
||||||
RUN npm install --global ts-node typescript @types/node
|
RUN npm install --global ts-node typescript @types/node
|
||||||
COPY ./ ./
|
COPY ./ ./
|
||||||
CMD ["ts-node", "solution.ts"]
|
CMD ["ts-node", "solution.ts"]
|
||||||
|
Loading…
Reference in New Issue
Block a user