git-truck 0.8.2 → 0.8.6-experimental
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bump-version.yml +1 -1
- package/README.md +17 -13
- package/cli.js +2 -0
- package/dev.js +4 -2
- package/package.json +4 -4
- package/server.ts +13 -8
- package/src/analyzer/analyze.server.ts +43 -76
- package/src/analyzer/analyze.test.ts +30 -30
- package/src/analyzer/args.server.ts +20 -6
- package/src/analyzer/constants.ts +1 -1
- package/src/analyzer/git-caller.server.ts +290 -0
- package/src/analyzer/hydrate.server.ts +1 -1
- package/src/analyzer/model.ts +13 -2
- package/src/analyzer/{util.ts → util.server.ts} +27 -33
- package/src/analyzer/util.test.ts +1 -1
- package/src/components/AnalyzingIndicator.tsx +55 -0
- package/src/components/Animations.ts +14 -0
- package/src/components/Chart.tsx +29 -8
- package/src/components/Details.tsx +8 -7
- package/src/components/GlobalInfo.tsx +19 -8
- package/src/components/HiddenFiles.tsx +3 -3
- package/src/components/Legend.tsx +1 -1
- package/src/components/LegendOther.tsx +42 -42
- package/src/components/Main.tsx +1 -1
- package/src/components/SearchBar.tsx +1 -6
- package/src/components/util.tsx +19 -10
- package/src/const.ts +6 -6
- package/src/contexts/ClickedContext.ts +17 -17
- package/src/contexts/DataContext.ts +12 -12
- package/src/contexts/MetricContext.ts +12 -12
- package/src/contexts/OptionsContext.ts +51 -51
- package/src/contexts/SearchContext.ts +19 -19
- package/src/lang-map.d.ts +3 -3
- package/src/metrics.ts +3 -2
- package/src/root.tsx +44 -1
- package/src/routes/{repo.tsx → $repo.tsx} +59 -15
- package/src/routes/index.tsx +156 -46
- package/build/index.js +0 -6836
- package/post-build.js +0 -14
- package/public/favicon.ico +0 -0
- package/src/analyzer/git-caller.ts +0 -117
- package/src/analyzer/index.ts +0 -4
|
@@ -31,7 +31,7 @@ jobs:
|
|
|
31
31
|
env:
|
|
32
32
|
GITHUB_TOKEN: ${{ secrets.TOKEN }}
|
|
33
33
|
|
|
34
|
-
- run: npm install --package-lock-only
|
|
34
|
+
- run: npm install --package-lock-only --ignore-scripts
|
|
35
35
|
- run: git add package-lock.json
|
|
36
36
|
- run: git config user.email "version-bot@example.com"
|
|
37
37
|
- run: git config user.name "Version Bot"
|
package/README.md
CHANGED
|
@@ -18,9 +18,9 @@ To check if these programs are installed, and what version you have, run `node -
|
|
|
18
18
|
|
|
19
19
|
## [Get started](#get-started)
|
|
20
20
|
|
|
21
|
-
1.
|
|
22
|
-
2.
|
|
23
|
-
3.
|
|
21
|
+
1. Within a git repository, or a directory containing git repositories, run the command `npx git-truck@latest`.
|
|
22
|
+
2. Press `y` if it asks you to download the package.
|
|
23
|
+
3. The application will now open in your default browser.
|
|
24
24
|
|
|
25
25
|
## [I got an error or I want to give feedback, what do i do?](#i-got-an-error-or-i-want-to-give-feedback-what-do-i-do)
|
|
26
26
|
|
|
@@ -38,19 +38,23 @@ npx git-truck [args]
|
|
|
38
38
|
|
|
39
39
|
### [Arguments](#arguments)
|
|
40
40
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
41
|
+
| arg | description | default value |
|
|
42
|
+
| :------------------: | :-------------------------------------------------------------------: | :----------------: |
|
|
43
|
+
| `--branch` | branch name | checked out branch |
|
|
44
|
+
| `--path` | path to a folder or a git repository | current directory |
|
|
45
|
+
| `--log` | output log level. See [here](./src/analyzer/log.server.ts) for values | - |
|
|
46
|
+
| `--port` | port to use for the program | 3000 |
|
|
47
|
+
| `--invalidate-cache` | bypass analyzer cache manually | - |
|
|
48
|
+
|
|
49
|
+
**Note:** Using `--invalidate-cache` will cause the analyzer to run every time the client talks to the server.
|
|
47
50
|
|
|
48
51
|
### [Configuration](#configuration)
|
|
49
52
|
|
|
50
53
|
You can add a `truckconfig.json` file to the root of your project, where you can define the arguments you want.
|
|
51
|
-
Additionally you can define which git-aliases should be considered as the same person.
|
|
54
|
+
Additionally you can define which git-aliases should be considered as the same person using `unionedAuthors`. If provided, the first name in the array is used as the name of the person.
|
|
52
55
|
You can also define files to ignore.
|
|
53
|
-
|
|
56
|
+
|
|
57
|
+
**Example:**
|
|
54
58
|
|
|
55
59
|
```json
|
|
56
60
|
{
|
|
@@ -60,7 +64,7 @@ Example:
|
|
|
60
64
|
["Bob", "Bobby Bob"],
|
|
61
65
|
["Alice", "aliiii", "alice alice"]
|
|
62
66
|
],
|
|
63
|
-
"hiddenFiles": ["package-lock.json", "*.bin", "*.svg"]
|
|
67
|
+
"hiddenFiles": ["package-lock.json", "*.bin", "*.svg"],
|
|
68
|
+
"invalidateCache": true
|
|
64
69
|
}
|
|
65
|
-
|
|
66
70
|
```
|
package/cli.js
ADDED
package/dev.js
CHANGED
|
@@ -6,12 +6,14 @@ const open = require("open")
|
|
|
6
6
|
const getPortLib = (await import("get-port"))
|
|
7
7
|
const getPort = getPortLib.default
|
|
8
8
|
const port = await getPort({
|
|
9
|
-
port: getPortLib.portNumbers(3000, 4000),
|
|
9
|
+
port: [80, ...getPortLib.portNumbers(3000, 4000)],
|
|
10
10
|
})
|
|
11
11
|
|
|
12
|
+
process.env["PORT"] = port.toString()
|
|
13
|
+
|
|
12
14
|
open("http://localhost:" + port)
|
|
13
15
|
await runAll(
|
|
14
|
-
[`dev:node --
|
|
16
|
+
[`dev:node -- ${process.argv.slice(2).join(" ")}`, "dev:remix"],
|
|
15
17
|
{
|
|
16
18
|
parallel: true,
|
|
17
19
|
stdout: process.stdout,
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "git-truck",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.6-experimental",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Visualizing a Git repository",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "./
|
|
8
|
-
"bin": "./
|
|
7
|
+
"main": "./cli.js",
|
|
8
|
+
"bin": "./cli.js",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "jest --coverage",
|
|
11
11
|
"clean": "rimraf -rf build public/build .cache .temp",
|
|
12
12
|
"tsc": "tsc",
|
|
13
|
-
"build": "remix setup node && cross-env NODE_ENV=production remix build
|
|
13
|
+
"build": "remix setup node && cross-env NODE_ENV=production remix build",
|
|
14
14
|
"dev": "cross-env NODE_ENV=development remix build && node dev.js",
|
|
15
15
|
"dev:remix": "cross-env NODE_ENV=development remix watch",
|
|
16
16
|
"postinstall": "npm run build",
|
package/server.ts
CHANGED
|
@@ -57,10 +57,7 @@ for usage instructions.`)
|
|
|
57
57
|
|
|
58
58
|
const staticAssetsPath = join(__dirname, "../public/build")
|
|
59
59
|
// Remix fingerprints its assets so we can cache forever.
|
|
60
|
-
app.use(
|
|
61
|
-
"/build",
|
|
62
|
-
express.static(staticAssetsPath, { immutable: true, maxAge: "1y" })
|
|
63
|
-
)
|
|
60
|
+
app.use("/build", express.static(staticAssetsPath, { immutable: true, maxAge: "1y" }))
|
|
64
61
|
|
|
65
62
|
// Everything else (like favicon.ico) is cached for an hour. You may want to be
|
|
66
63
|
// more aggressive with this caching.
|
|
@@ -76,16 +73,24 @@ for usage instructions.`)
|
|
|
76
73
|
})
|
|
77
74
|
)
|
|
78
75
|
|
|
76
|
+
let devServerPort: number | null = null
|
|
77
|
+
let userHasProvidedPort = false
|
|
79
78
|
let minPort = 3000
|
|
80
79
|
|
|
81
|
-
if (args.port && !isNaN(parseInt(args.port)))
|
|
80
|
+
if (args.port && !isNaN(parseInt(args.port))) {
|
|
81
|
+
minPort = parseInt(args.port)
|
|
82
|
+
userHasProvidedPort = true
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (process.env["PORT"] && !isNaN(parseInt(process.env["PORT"]))) {
|
|
86
|
+
devServerPort = parseInt(process.env["PORT"])
|
|
87
|
+
}
|
|
82
88
|
|
|
83
89
|
const getPortLib = await import("get-port")
|
|
84
90
|
const getPort = getPortLib.default
|
|
85
91
|
const port = await getPort({
|
|
86
|
-
port: getPortLib.portNumbers(minPort, minPort + 1000),
|
|
92
|
+
port: devServerPort ?? [...(!userHasProvidedPort ? [80] : []), ...getPortLib.portNumbers(minPort, minPort + 1000)],
|
|
87
93
|
})
|
|
88
|
-
|
|
89
94
|
app.listen(port).once("listening", () => printOpen(port))
|
|
90
95
|
})()
|
|
91
96
|
|
|
@@ -93,7 +98,7 @@ async function printOpen(port: number) {
|
|
|
93
98
|
console.log()
|
|
94
99
|
console.log(`Now listening on port ${port}`)
|
|
95
100
|
if (process.env.NODE_ENV !== "development") {
|
|
96
|
-
const url =
|
|
101
|
+
const url = `http://localhost:${port}`
|
|
97
102
|
console.log(`Opening ${url} in your browser`)
|
|
98
103
|
await open(url)
|
|
99
104
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { promises as fs } from "fs"
|
|
2
2
|
import {
|
|
3
3
|
GitBlobObject,
|
|
4
4
|
GitCommitObject,
|
|
@@ -7,25 +7,16 @@ import {
|
|
|
7
7
|
AnalyzerData,
|
|
8
8
|
AnalyzerDataInterfaceVersion,
|
|
9
9
|
TruckUserConfig,
|
|
10
|
+
TruckConfig,
|
|
10
11
|
} from "./model"
|
|
11
12
|
import { log, setLogLevel } from "./log.server"
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
formatMs,
|
|
15
|
-
writeRepoToFile,
|
|
16
|
-
getCurrentBranch,
|
|
17
|
-
getRepoName,
|
|
18
|
-
getDefaultGitSettingValue,
|
|
19
|
-
resetGitSetting,
|
|
20
|
-
setGitSetting,
|
|
21
|
-
} from "./util"
|
|
22
|
-
import { GitCaller } from "./git-caller"
|
|
13
|
+
import { describeAsyncJob, formatMs, writeRepoToFile, getDirName } from "./util.server"
|
|
14
|
+
import { GitCaller } from "./git-caller.server"
|
|
23
15
|
import { emptyGitCommitHash } from "./constants"
|
|
24
|
-
import { resolve, isAbsolute
|
|
16
|
+
import { resolve, isAbsolute } from "path"
|
|
25
17
|
import { performance } from "perf_hooks"
|
|
26
18
|
import { getAuthorSet, hydrateData } from "./hydrate.server"
|
|
27
19
|
import {} from "@remix-run/node"
|
|
28
|
-
import { getArgs } from "./args.server"
|
|
29
20
|
import ignore from "ignore"
|
|
30
21
|
import { applyIgnore, applyMetrics, initMetrics, TreeCleanup } from "./postprocessing.server"
|
|
31
22
|
import latestVersion from "latest-version"
|
|
@@ -35,25 +26,6 @@ import { exec } from "child_process"
|
|
|
35
26
|
|
|
36
27
|
let repoDir = "."
|
|
37
28
|
|
|
38
|
-
export async function findBranchHead(repo: string, branch: string | null) {
|
|
39
|
-
if (branch === null) branch = await getCurrentBranch(repo)
|
|
40
|
-
|
|
41
|
-
const gitFolder = join(repo, ".git")
|
|
42
|
-
if (!fsSync.existsSync(gitFolder)) {
|
|
43
|
-
throw Error(`${repo} is not a git repository`)
|
|
44
|
-
}
|
|
45
|
-
// Find file containing the branch head
|
|
46
|
-
const branchPath = join(gitFolder, "refs/heads/" + branch)
|
|
47
|
-
const absolutePath = join(process.cwd(), branchPath)
|
|
48
|
-
log.debug("Looking for branch head at " + absolutePath)
|
|
49
|
-
|
|
50
|
-
const branchHead = (await fs.readFile(branchPath, "utf-8")).trim()
|
|
51
|
-
log.debug(`${branch} -> [commit]${branchHead}`)
|
|
52
|
-
if (!branchHead) throw Error("Branch head not found")
|
|
53
|
-
|
|
54
|
-
return [branchHead, branch]
|
|
55
|
-
}
|
|
56
|
-
|
|
57
29
|
export async function analyzeCommitLight(hash: string): Promise<GitCommitObjectLight> {
|
|
58
30
|
const rawContent = await GitCaller.getInstance().catFileCached(hash)
|
|
59
31
|
const commitRegex =
|
|
@@ -181,9 +153,9 @@ export async function updateTruckConfig(repoDir: string, updaterFn: (tc: TruckUs
|
|
|
181
153
|
await fs.writeFile(truckConfigPath, JSON.stringify(updatedConfig, null, 2))
|
|
182
154
|
}
|
|
183
155
|
|
|
184
|
-
export async function analyze(
|
|
185
|
-
const args = await getArgs()
|
|
156
|
+
export async function analyze(args: TruckConfig) {
|
|
186
157
|
GitCaller.initInstance(args.path)
|
|
158
|
+
const git = GitCaller.getInstance()
|
|
187
159
|
|
|
188
160
|
if (args?.log) {
|
|
189
161
|
setLogLevel(args.log as string)
|
|
@@ -197,76 +169,74 @@ export async function analyze(useCache = true) {
|
|
|
197
169
|
const hiddenFiles = args.hiddenFiles
|
|
198
170
|
|
|
199
171
|
const start = performance.now()
|
|
200
|
-
const [
|
|
201
|
-
() => findBranchHead(
|
|
172
|
+
const [findBranchHeadResult, findBranchHeadError] = await describeAsyncJob(
|
|
173
|
+
() => git.findBranchHead(branch),
|
|
202
174
|
"Finding branch head",
|
|
203
175
|
"Found branch head",
|
|
204
176
|
"Error finding branch head"
|
|
205
177
|
)
|
|
206
|
-
const repoName =
|
|
207
|
-
|
|
208
|
-
let data: AnalyzerData | null = null
|
|
178
|
+
const repoName = getDirName(repoDir)
|
|
209
179
|
|
|
210
|
-
|
|
211
|
-
if (fsSync.existsSync(dataPath)) {
|
|
212
|
-
const path = getOutPathFromRepoAndBranch(repoName, branchName)
|
|
213
|
-
const cachedData = JSON.parse(await fs.readFile(path, "utf8")) as AnalyzerData
|
|
180
|
+
if (findBranchHeadError) throw findBranchHeadError
|
|
214
181
|
|
|
215
|
-
|
|
216
|
-
const branchHeadMatches = branchHead === cachedData.commit.hash
|
|
182
|
+
const [branchHead, branchName] = findBranchHeadResult
|
|
217
183
|
|
|
218
|
-
|
|
219
|
-
const dataVersionMatches = cachedData.interfaceVersion === AnalyzerDataInterfaceVersion
|
|
184
|
+
let data: AnalyzerData | null = null
|
|
220
185
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
186
|
+
if (!args.invalidateCache) {
|
|
187
|
+
const [cachedData, reasons] = await GitCaller.retrieveCachedResult({
|
|
188
|
+
repo: repoName,
|
|
189
|
+
branch: branchName,
|
|
190
|
+
branchHead: branchHead,
|
|
191
|
+
})
|
|
226
192
|
|
|
227
|
-
|
|
228
|
-
if (Object.values(cacheConditions).every(Boolean)) {
|
|
193
|
+
if (cachedData) {
|
|
229
194
|
data = {
|
|
230
195
|
...cachedData,
|
|
231
196
|
hiddenFiles,
|
|
232
197
|
}
|
|
233
198
|
} else {
|
|
234
|
-
|
|
235
|
-
.
|
|
236
|
-
|
|
237
|
-
.join(", ")
|
|
238
|
-
log.info(`Reanalyzing, since the following cache conditions were not met: ${reasons}`)
|
|
199
|
+
log.info(
|
|
200
|
+
`Reanalyzing, since the following cache conditions were not met:\n${reasons.map((r) => ` - ${r}`).join("\n")}`
|
|
201
|
+
)
|
|
239
202
|
}
|
|
203
|
+
} else {
|
|
204
|
+
GitCaller.getInstance().setUseCache(false)
|
|
240
205
|
}
|
|
241
206
|
|
|
242
207
|
if (data === null) {
|
|
243
|
-
const quotePathDefaultValue = await getDefaultGitSettingValue(
|
|
244
|
-
await setGitSetting(
|
|
245
|
-
const renamesDefaultValue = await getDefaultGitSettingValue(
|
|
246
|
-
await setGitSetting(
|
|
247
|
-
const renameLimitDefaultValue = await getDefaultGitSettingValue(
|
|
248
|
-
await setGitSetting(
|
|
208
|
+
const quotePathDefaultValue = await git.getDefaultGitSettingValue("core.quotepath")
|
|
209
|
+
await git.setGitSetting("core.quotePath", "off")
|
|
210
|
+
const renamesDefaultValue = await git.getDefaultGitSettingValue("diff.renames")
|
|
211
|
+
await git.setGitSetting("diff.renames", "true")
|
|
212
|
+
const renameLimitDefaultValue = await git.getDefaultGitSettingValue("diff.renameLimit")
|
|
213
|
+
await git.setGitSetting("diff.renameLimit", "1000000")
|
|
214
|
+
const hasUnstagedChanges = await git.hasUnstagedChanges()
|
|
249
215
|
|
|
250
216
|
const runDateEpoch = Date.now()
|
|
251
|
-
const repoTree = await describeAsyncJob(
|
|
217
|
+
const [repoTree, repoTreeError] = await describeAsyncJob(
|
|
252
218
|
() => analyzeCommit(repoName, branchHead),
|
|
253
219
|
"Analyzing commit tree",
|
|
254
220
|
"Commit tree analyzed",
|
|
255
221
|
"Error analyzing commit tree"
|
|
256
222
|
)
|
|
257
223
|
|
|
258
|
-
|
|
224
|
+
if (repoTreeError) throw repoTreeError
|
|
225
|
+
|
|
226
|
+
const [hydratedRepoTree, hydratedRepoTreeError] = await describeAsyncJob(
|
|
259
227
|
() => hydrateData(repoDir, repoTree),
|
|
260
228
|
"Hydrating commit tree",
|
|
261
229
|
"Commit tree hydrated",
|
|
262
230
|
"Error hydrating commit tree"
|
|
263
231
|
)
|
|
264
232
|
|
|
265
|
-
await resetGitSetting(
|
|
266
|
-
await resetGitSetting(
|
|
267
|
-
await resetGitSetting(
|
|
233
|
+
await git.resetGitSetting("core.quotepath", quotePathDefaultValue)
|
|
234
|
+
await git.resetGitSetting("diff.renames", renamesDefaultValue)
|
|
235
|
+
await git.resetGitSetting("diff.renameLimit", renameLimitDefaultValue)
|
|
236
|
+
|
|
237
|
+
const defaultOutPath = GitCaller.getCachePath(repoName, branchName)
|
|
238
|
+
if (hydratedRepoTreeError) throw hydratedRepoTreeError
|
|
268
239
|
|
|
269
|
-
const defaultOutPath = getOutPathFromRepoAndBranch(repoName, branchName)
|
|
270
240
|
let outPath = resolve((args.out as string) ?? defaultOutPath)
|
|
271
241
|
if (!isAbsolute(outPath)) outPath = resolve(process.cwd(), outPath)
|
|
272
242
|
|
|
@@ -289,6 +259,7 @@ export async function analyze(useCache = true) {
|
|
|
289
259
|
currentVersion: pkg.version,
|
|
290
260
|
latestVersion: latestV,
|
|
291
261
|
lastRunEpoch: runDateEpoch,
|
|
262
|
+
hasUnstagedChanges,
|
|
292
263
|
}
|
|
293
264
|
|
|
294
265
|
await describeAsyncJob(
|
|
@@ -315,7 +286,3 @@ export async function analyze(useCache = true) {
|
|
|
315
286
|
|
|
316
287
|
return data
|
|
317
288
|
}
|
|
318
|
-
|
|
319
|
-
export function getOutPathFromRepoAndBranch(repoName: string, branchName: string) {
|
|
320
|
-
return resolve(__dirname, "..", ".temp", repoName, `${branchName}.json`)
|
|
321
|
-
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import { getCoAuthors } from "./coauthors.server"
|
|
2
|
-
|
|
3
|
-
describe("getCoAuthors", () => {
|
|
4
|
-
it("Should return none", () => {
|
|
5
|
-
const actual = getCoAuthors("lorem ipsum\n\nCo-authored-by:")
|
|
6
|
-
expect(actual.length).toBe(0)
|
|
7
|
-
})
|
|
8
|
-
|
|
9
|
-
it("Should return none when empty input", () => {
|
|
10
|
-
const actual = getCoAuthors("")
|
|
11
|
-
expect(actual.length).toBe(0)
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it("Should return 2 authors", () => {
|
|
15
|
-
const sampleDescription =
|
|
16
|
-
"did some stuff\n\nCo-authored-by: Bob Bobby <bob@example.com>\nCo-authored-by: Alice Lmao <alice@example.com>"
|
|
17
|
-
const expected = [
|
|
18
|
-
{
|
|
19
|
-
name: "Bob Bobby",
|
|
20
|
-
email: "bob@example.com",
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "Alice Lmao",
|
|
24
|
-
email: "alice@example.com",
|
|
25
|
-
},
|
|
26
|
-
]
|
|
27
|
-
const actual = getCoAuthors(sampleDescription)
|
|
28
|
-
expect(actual).toStrictEqual(expected)
|
|
29
|
-
})
|
|
30
|
-
})
|
|
1
|
+
import { getCoAuthors } from "./coauthors.server"
|
|
2
|
+
|
|
3
|
+
describe("getCoAuthors", () => {
|
|
4
|
+
it("Should return none", () => {
|
|
5
|
+
const actual = getCoAuthors("lorem ipsum\n\nCo-authored-by:")
|
|
6
|
+
expect(actual.length).toBe(0)
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
it("Should return none when empty input", () => {
|
|
10
|
+
const actual = getCoAuthors("")
|
|
11
|
+
expect(actual.length).toBe(0)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it("Should return 2 authors", () => {
|
|
15
|
+
const sampleDescription =
|
|
16
|
+
"did some stuff\n\nCo-authored-by: Bob Bobby <bob@example.com>\nCo-authored-by: Alice Lmao <alice@example.com>"
|
|
17
|
+
const expected = [
|
|
18
|
+
{
|
|
19
|
+
name: "Bob Bobby",
|
|
20
|
+
email: "bob@example.com",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "Alice Lmao",
|
|
24
|
+
email: "alice@example.com",
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
const actual = getCoAuthors(sampleDescription)
|
|
28
|
+
expect(actual).toStrictEqual(expected)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
@@ -2,6 +2,9 @@ import yargsParser from "yargs-parser"
|
|
|
2
2
|
import { promises as fs } from "fs"
|
|
3
3
|
import { resolve } from "path"
|
|
4
4
|
import { TruckConfig, TruckUserConfig } from "./model"
|
|
5
|
+
import { GitCaller } from "./git-caller.server"
|
|
6
|
+
import { getBaseDirFromPath } from "./util.server"
|
|
7
|
+
import { log } from "./log.server"
|
|
5
8
|
|
|
6
9
|
export function parseArgs(rawArgs: string[] = process.argv.slice(2)) {
|
|
7
10
|
return yargsParser(rawArgs, {
|
|
@@ -11,24 +14,35 @@ export function parseArgs(rawArgs: string[] = process.argv.slice(2)) {
|
|
|
11
14
|
})
|
|
12
15
|
}
|
|
13
16
|
|
|
14
|
-
export async function
|
|
17
|
+
export async function getArgsWithDefaults(): Promise<TruckConfig> {
|
|
15
18
|
const args = parseArgs()
|
|
16
19
|
const tempArgs = {
|
|
17
20
|
path: ".",
|
|
18
|
-
branch: null,
|
|
19
21
|
hiddenFiles: [] as string[],
|
|
20
22
|
unionedAuthors: [] as string[][],
|
|
23
|
+
invalidateCache: false,
|
|
21
24
|
...args,
|
|
22
25
|
}
|
|
23
26
|
|
|
27
|
+
return tempArgs
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function getTruckConfigWithArgs(repo: string): Promise<TruckConfig> {
|
|
31
|
+
const args = await getArgsWithDefaults()
|
|
32
|
+
|
|
33
|
+
const pathIsRepo = await GitCaller.isGitRepo(args.path)
|
|
34
|
+
args.path = pathIsRepo ? getBaseDirFromPath(args.path) : args.path
|
|
35
|
+
|
|
24
36
|
let config: TruckUserConfig = {}
|
|
25
37
|
try {
|
|
26
|
-
const configContents = JSON.parse(await fs.readFile(resolve(
|
|
38
|
+
const configContents = JSON.parse(await fs.readFile(resolve(args.path, repo, "truckconfig.json"), "utf-8"))
|
|
27
39
|
config = configContents
|
|
28
|
-
} catch (e) {
|
|
40
|
+
} catch (e) {
|
|
41
|
+
log.warn(`No truckconfig.json found in repo ${repo}`)
|
|
42
|
+
}
|
|
29
43
|
|
|
30
44
|
return {
|
|
31
|
-
...
|
|
45
|
+
...args,
|
|
32
46
|
...config,
|
|
33
|
-
}
|
|
47
|
+
}
|
|
34
48
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const emptyGitCommitHash = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|
|
1
|
+
export const emptyGitCommitHash = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
|