-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcommits.ts
More file actions
120 lines (103 loc) · 3.4 KB
/
Copy pathcommits.ts
File metadata and controls
120 lines (103 loc) · 3.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import { mkdir, exists, rmdir } from 'fs'
import util from 'util'
import * as path from 'path'
import { LogResult } from 'simple-git/typings/response'
import gitP, { SimpleGit } from 'simple-git/promise'
import { validateCommitOrder } from './validateCommits'
const mkdirPromise = util.promisify(mkdir)
const existsPromise = util.promisify(exists)
const rmdirPromise = util.promisify(rmdir)
type GetCommitOptions = {
localDir: string
codeBranch: string
}
export type CommitLogObject = { [position: string]: string[] }
export function parseCommits (
logs: LogResult<any>
): { [hash: string]: string[] } {
// Filter relevant logs
const commits: CommitLogObject = {}
const positions: string[] = []
for (const commit of logs.all) {
const matches = commit.message.match(
/^(?<init>INIT)|(L?(?<levelId>\d+)[S|\.]?(?<stepId>\d+)?(?<stepType>[Q|A|T|S])?)/
)
if (matches && matches.length) {
// Use an object of commit arrays to collect all commits
const { groups } = matches
let position
if (groups.init) {
position = 'INIT'
} else if (groups.levelId && groups.stepId) {
let stepType
// @deprecated Q
if (!groups.stepType || ['Q', 'T'].includes(groups.stepType)) {
stepType = 'T' // test
// @deprecated A
} else if (!groups.stepType || ['A', 'S'].includes(groups.stepType)) {
stepType = 'S' // solution
}
position = `${groups.levelId}.${groups.stepId}:${stepType}`
} else if (groups.levelId) {
position = groups.levelId
} else {
console.warn(`No matcher for commit "${commit.message}"`)
}
commits[position] = [...(commits[position] || []), commit.hash]
positions.unshift(position)
} else {
const initMatches = commit.message.match(/^INIT/)
if (initMatches && initMatches.length) {
commits.INIT = [commit.hash, ...(commits.INIT || [])]
positions.unshift('INIT')
}
}
}
// validate order
validateCommitOrder(positions)
return commits
}
export async function getCommits ({
localDir,
codeBranch
}: GetCommitOptions): Promise<CommitLogObject> {
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 existsPromise(tmpDir)
if (tmpDirExists) {
await rmdirPromise(tmpDir, { recursive: true })
}
await mkdirPromise(tmpDir)
const tempGit = gitP(tmpDir)
await tempGit.clone(localDir, tmpDir)
const branches = await git.branch()
if (!branches.all.length) {
throw new Error('No branches found')
} else if (!branches.all.includes(codeBranch)) {
throw new Error(`Code branch "${codeBranch}" not found`)
}
// track the original branch in case of failure
const originalBranch = branches.current
try {
// Checkout the code branches
await git.checkout(codeBranch)
// Load all logs
const logs = await git.log()
const commits = parseCommits(logs)
return commits
} catch (e) {
console.error('Error with checkout or commit matching')
throw new Error(e.message)
} finally {
// revert back to the original branch on failure
await git.checkout(originalBranch)
// cleanup the tmp directory
await rmdirPromise(tmpDir, { recursive: true })
}
}