2021-06-25 12:46:01 +02:00
|
|
|
import execa from 'execa'
|
2021-06-09 20:31:45 +02:00
|
|
|
|
2021-11-10 18:57:10 +01:00
|
|
|
import { Challenge } from './Challenge'
|
2021-06-09 20:31:45 +02:00
|
|
|
import { Solution } from './Solution'
|
|
|
|
|
|
|
|
const solutionsRegex = new RegExp(
|
2021-06-24 22:10:08 +02:00
|
|
|
/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)/
|
2021-06-09 20:31:45 +02:00
|
|
|
)
|
|
|
|
|
2021-11-09 16:45:42 +01:00
|
|
|
const dockerRegex = new RegExp(
|
|
|
|
/templates\/docker\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/Dockerfile/
|
|
|
|
)
|
|
|
|
|
2021-11-10 18:57:10 +01:00
|
|
|
const inputOutputRegex = new RegExp(
|
|
|
|
/challenges\/[\s\S]*\/test\/[0-9]\/(input.txt|output.txt)/
|
|
|
|
)
|
|
|
|
|
2021-06-25 12:46:01 +02:00
|
|
|
export interface GitAffectedOptions {
|
|
|
|
isContinuousIntegration: boolean
|
2021-06-30 15:23:58 +02:00
|
|
|
base?: string
|
2021-06-25 12:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export class GitAffected implements GitAffectedOptions {
|
|
|
|
public isContinuousIntegration: boolean
|
2021-06-30 15:23:58 +02:00
|
|
|
public base?: string
|
2021-06-25 12:46:01 +02:00
|
|
|
|
|
|
|
constructor (options: GitAffectedOptions) {
|
|
|
|
this.isContinuousIntegration = options.isContinuousIntegration
|
2021-06-30 15:23:58 +02:00
|
|
|
this.base = options.base
|
2021-06-25 12:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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<string[]> {
|
2021-06-25 13:02:34 +02:00
|
|
|
try {
|
|
|
|
const { stdout } = await execa.command(
|
|
|
|
`git diff --name-only --relative ${base} ${head}`
|
|
|
|
)
|
|
|
|
return this.parseGitOutput(stdout)
|
|
|
|
} catch {
|
|
|
|
return []
|
|
|
|
}
|
2021-06-25 12:46:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public async getUncommittedFiles (): Promise<string[]> {
|
|
|
|
return await this.getFilesUsingBaseAndHead('HEAD', '.')
|
|
|
|
}
|
|
|
|
|
|
|
|
public async getLatestPushedCommit (): Promise<string> {
|
|
|
|
const latestCommit = this.isContinuousIntegration ? '~1' : ''
|
|
|
|
const { stdout } = await execa.command(`git rev-parse origin/master${latestCommit}`)
|
|
|
|
return stdout
|
|
|
|
}
|
|
|
|
|
|
|
|
public async getUnpushedFiles (): Promise<string[]> {
|
|
|
|
return await this.getFilesUsingBaseAndHead(
|
|
|
|
await this.getLatestPushedCommit(),
|
|
|
|
'.'
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-06-09 20:31:45 +02:00
|
|
|
public async getAffectedSolutions (): Promise<Solution[]> {
|
2021-06-30 15:23:58 +02:00
|
|
|
let files = [
|
|
|
|
...(await this.getUnpushedFiles()),
|
|
|
|
...(await this.getUncommittedFiles())
|
|
|
|
]
|
|
|
|
if (this.base != null) {
|
|
|
|
files.push(...(await this.getFilesUsingBaseAndHead(this.base, '.')))
|
|
|
|
}
|
|
|
|
files = Array.from(new Set(files))
|
2021-06-25 12:46:01 +02:00
|
|
|
const affectedSolutionsPaths = files.filter((filePath) => {
|
|
|
|
return solutionsRegex.test(filePath)
|
2021-06-09 20:31:45 +02:00
|
|
|
})
|
2021-11-09 16:45:42 +01:00
|
|
|
const affectedDockerPaths = files.filter((filePath) => {
|
|
|
|
return dockerRegex.test(filePath)
|
|
|
|
})
|
|
|
|
const affectedLanguages = affectedDockerPaths.map((filePath) => {
|
|
|
|
const [,, programmingLanguageName] = filePath.replaceAll('\\', '/').split('/')
|
|
|
|
return programmingLanguageName
|
|
|
|
})
|
2021-11-10 18:57:10 +01:00
|
|
|
const affectedInputOutput = files.filter((filePath) => {
|
|
|
|
return inputOutputRegex.test(filePath)
|
|
|
|
})
|
|
|
|
const affectedChallenges = affectedInputOutput.map((filePath) => {
|
|
|
|
const [, challengeName] = filePath.replaceAll('\\', '/').split('/')
|
|
|
|
return new Challenge({ name: challengeName })
|
|
|
|
})
|
2021-11-09 16:45:42 +01:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
2021-11-10 18:57:10 +01:00
|
|
|
for (const challenge of affectedChallenges) {
|
|
|
|
let isSolutionIncluded = false
|
|
|
|
for (const solution of solutions) {
|
|
|
|
if (solution.challenge.name === challenge.name) {
|
|
|
|
isSolutionIncluded = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!isSolutionIncluded) {
|
|
|
|
const solutionsByChallenge = await Solution.getManyByChallenge(challenge)
|
|
|
|
solutions.push(...solutionsByChallenge)
|
|
|
|
}
|
|
|
|
}
|
2021-11-09 16:45:42 +01:00
|
|
|
return solutions
|
2021-06-09 20:31:45 +02:00
|
|
|
}
|
|
|
|
}
|