-
-
Notifications
You must be signed in to change notification settings - Fork 577
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use spawnSync instead of execaSync in
git.ts
(#1347)
Co-authored-by: Chris Swithinbank <[email protected]>
- Loading branch information
1 parent
bbf9998
commit 8994d00
Showing
7 changed files
with
152 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@astrojs/starlight': patch | ||
--- | ||
|
||
Refactor `getLastUpdated` to use `node:child_process` instead of `execa`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { mkdtempSync, writeFileSync } from 'node:fs'; | ||
import { join } from 'node:path'; | ||
import { tmpdir } from 'node:os'; | ||
import { spawnSync } from 'node:child_process'; | ||
import { describe, expect, test } from 'vitest'; | ||
import { getNewestCommitDate } from '../../utils/git'; | ||
|
||
describe('getNewestCommitDate', () => { | ||
const { commitAllChanges, getFilePath, writeFile } = makeTestRepo(); | ||
|
||
test('returns the newest commit date', () => { | ||
const file = 'updated.md'; | ||
const lastCommitDate = '2023-06-25'; | ||
|
||
writeFile(file, 'content 0'); | ||
commitAllChanges('add updated.md', '2023-06-21'); | ||
writeFile(file, 'content 1'); | ||
commitAllChanges('update updated.md', lastCommitDate); | ||
|
||
expectCommitDateToEqual(getNewestCommitDate(getFilePath(file)), lastCommitDate); | ||
}); | ||
|
||
test('returns the initial commit date for a file never updated', () => { | ||
const file = 'added.md'; | ||
const commitDate = '2022-09-18'; | ||
|
||
writeFile(file, 'content'); | ||
commitAllChanges('add added.md', commitDate); | ||
|
||
expectCommitDateToEqual(getNewestCommitDate(getFilePath(file)), commitDate); | ||
}); | ||
|
||
test('returns the newest commit date for a file with a name that contains a space', () => { | ||
const file = 'updated with space.md'; | ||
const lastCommitDate = '2021-01-02'; | ||
|
||
writeFile(file, 'content 0'); | ||
commitAllChanges('add updated.md', '2021-01-01'); | ||
writeFile(file, 'content 1'); | ||
commitAllChanges('update updated.md', lastCommitDate); | ||
|
||
expectCommitDateToEqual(getNewestCommitDate(getFilePath(file)), lastCommitDate); | ||
}); | ||
|
||
test('returns the newest commit date for a file updated the same day', () => { | ||
const file = 'updated-same-day.md'; | ||
const lastCommitDate = '2023-06-25T14:22:35Z'; | ||
|
||
writeFile(file, 'content 0'); | ||
commitAllChanges('add updated.md', '2023-06-25T12:34:56Z'); | ||
writeFile(file, 'content 1'); | ||
commitAllChanges('update updated.md', lastCommitDate); | ||
|
||
expectCommitDateToEqual(getNewestCommitDate(getFilePath(file)), lastCommitDate); | ||
}); | ||
|
||
test('throws when failing to retrieve the git history for a file', () => { | ||
expect(() => getNewestCommitDate(getFilePath('../not-a-starlight-test-repo/test.md'))).toThrow( | ||
/^Failed to retrieve the git history for file "[/\\-\w ]+\/test\.md"/ | ||
); | ||
}); | ||
|
||
test('throws when trying to get the history of a non-existing or untracked file', () => { | ||
const expectedError = | ||
/^Failed to validate the timestamp for file "[/\\-\w ]+\/(?:unknown|untracked)\.md"$/; | ||
writeFile('untracked.md', 'content'); | ||
|
||
expect(() => getNewestCommitDate(getFilePath('unknown.md'))).toThrow(expectedError); | ||
expect(() => getNewestCommitDate(getFilePath('untracked.md'))).toThrow(expectedError); | ||
}); | ||
}); | ||
|
||
function expectCommitDateToEqual(commitDate: CommitDate, expectedDateStr: ISODate) { | ||
const expectedDate = new Date(expectedDateStr); | ||
expect(commitDate).toStrictEqual(expectedDate); | ||
} | ||
|
||
function makeTestRepo() { | ||
const repoPath = mkdtempSync(join(tmpdir(), 'starlight-test-git-')); | ||
|
||
function runInRepo(command: string, args: string[], env: NodeJS.ProcessEnv = {}) { | ||
const result = spawnSync(command, args, { cwd: repoPath, env }); | ||
|
||
if (result.status !== 0) { | ||
throw new Error(`Failed to execute test repository command: '${command} ${args.join(' ')}'`); | ||
} | ||
} | ||
|
||
// Configure git specifically for this test repository. | ||
runInRepo('git', ['init']); | ||
runInRepo('git', ['config', 'user.name', 'starlight-test']); | ||
runInRepo('git', ['config', 'user.email', '[email protected]']); | ||
runInRepo('git', ['config', 'commit.gpgsign', 'false']); | ||
|
||
return { | ||
// The `dateStr` argument should be in the `YYYY-MM-DD` or `YYYY-MM-DDTHH:MM:SSZ` format. | ||
commitAllChanges(message: string, dateStr: ISODate) { | ||
const date = dateStr.endsWith('Z') ? dateStr : `${dateStr}T00:00:00Z`; | ||
|
||
runInRepo('git', ['add', '-A']); | ||
// This sets both the author and committer dates to the provided date. | ||
runInRepo('git', ['commit', '-m', message, '--date', date], { GIT_COMMITTER_DATE: date }); | ||
}, | ||
getFilePath(name: string) { | ||
return join(repoPath, name); | ||
}, | ||
writeFile(name: string, content: string) { | ||
writeFileSync(join(repoPath, name), content); | ||
}, | ||
}; | ||
} | ||
|
||
type ISODate = | ||
| `${number}-${number}-${number}` | ||
| `${number}-${number}-${number}T${number}:${number}:${number}Z`; | ||
|
||
type CommitDate = ReturnType<typeof getNewestCommitDate>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,24 @@ | ||
/** | ||
* Heavily inspired by | ||
* https://github.com/facebook/docusaurus/blob/46d2aa231ddb18ed67311b6195260af46d7e8bdc/packages/docusaurus-utils/src/gitUtils.ts | ||
* | ||
* Copyright (c) Facebook, Inc. and its affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
import { basename, dirname } from 'node:path'; | ||
import { execaSync } from 'execa'; | ||
import { spawnSync } from 'node:child_process'; | ||
|
||
/** Custom error thrown when the current file is not tracked by git. */ | ||
class FileNotTrackedError extends Error {} | ||
export function getNewestCommitDate(file: string) { | ||
const result = spawnSync('git', ['log', '--format=%ct', '--max-count=1', basename(file)], { | ||
cwd: dirname(file), | ||
encoding: 'utf-8', | ||
}); | ||
|
||
/** | ||
* Fetches the git history of a file and returns a relevant commit date. | ||
* It gets the commit date instead of author date so that amended commits | ||
* can have their dates updated. | ||
* | ||
* @throws {FileNotTrackedError} If the current file is not tracked by git. | ||
* @throws Also throws when `git log` exited with non-zero, or when it outputs | ||
* unexpected text. | ||
*/ | ||
export function getFileCommitDate( | ||
file: string, | ||
age: 'oldest' | 'newest' = 'oldest' | ||
): { | ||
date: Date; | ||
timestamp: number; | ||
} { | ||
const result = execaSync( | ||
'git', | ||
[ | ||
'log', | ||
`--format=%ct`, | ||
'--max-count=1', | ||
...(age === 'oldest' ? ['--follow', '--diff-filter=A'] : []), | ||
'--', | ||
basename(file), | ||
], | ||
{ | ||
cwd: dirname(file), | ||
} | ||
); | ||
if (result.exitCode !== 0) { | ||
throw new Error( | ||
`Failed to retrieve the git history for file "${file}" with exit code ${result.exitCode}: ${result.stderr}` | ||
); | ||
if (result.error) { | ||
throw new Error(`Failed to retrieve the git history for file "${file}"`); | ||
} | ||
|
||
const output = result.stdout.trim(); | ||
|
||
if (!output) { | ||
throw new FileNotTrackedError( | ||
`Failed to retrieve the git history for file "${file}" because the file is not tracked by git.` | ||
); | ||
} | ||
|
||
const regex = /^(?<timestamp>\d+)$/; | ||
const match = output.match(regex); | ||
|
||
if (!match) { | ||
throw new Error( | ||
`Failed to retrieve the git history for file "${file}" with unexpected output: ${output}` | ||
); | ||
if (!match?.groups?.timestamp) { | ||
throw new Error(`Failed to validate the timestamp for file "${file}"`); | ||
} | ||
|
||
const timestamp = Number(match.groups!.timestamp); | ||
const timestamp = Number(match.groups.timestamp); | ||
const date = new Date(timestamp * 1000); | ||
|
||
return { date, timestamp }; | ||
return date; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8994d00
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
starlight-i18n – ./docs/lunaria
starlight-i18n-astrodotbuild.vercel.app
starlight-i18n-git-main-astrodotbuild.vercel.app
starlight-i18n.vercel.app
i18n.starlight.astro.build