Skip to content

Commit 640f04c

Browse files
authored
Merge pull request #9480 from keymanapp/feat/developer/use-last-commit-date-for-keyboard_info
feat(developer): use git's last commit date for keyboard_info 🎺
2 parents 0cd3bd6 + ccfefa2 commit 640f04c

File tree

4 files changed

+74
-3
lines changed

4 files changed

+74
-3
lines changed

developer/src/kmc-keyboard-info/src/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ export interface KeyboardInfoSources {
5151

5252
/** The source package filename and relative path (.kps) */
5353
kpsFilename: string;
54+
55+
/** Last modification date for files in the project folder 'YYYY-MM-DDThh:mm:ssZ' */
56+
lastCommitDate?: string;
5457
};
5558

5659
export class KeyboardInfoCompiler {
@@ -164,8 +167,8 @@ export class KeyboardInfoCompiler {
164167

165168
this.fillLanguages(keyboard_info, kmpJsonData);
166169

167-
// TODO: use: TZ=UTC0 git log -1 --no-merges --date=format:%Y-%m-%dT%H:%M:%SZ --format=%ad
168-
keyboard_info.lastModifiedDate = (new Date).toISOString();
170+
// If a last commit date is not given, then just use the current time
171+
keyboard_info.lastModifiedDate = sources.lastCommitDate ?? (new Date).toISOString();
169172

170173
keyboard_info.packageFilename = this.callbacks.path.basename(sources.kmpFilename);
171174

developer/src/kmc/src/commands/buildClasses/BuildKeyboardInfo.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { KeyboardInfoCompiler } from '@keymanapp/kmc-keyboard-info';
55
import { loadProject } from '../../util/projectLoader.js';
66
import { InfrastructureMessages } from '../../messages/infrastructureMessages.js';
77
import { calculateSourcePath } from '../../util/calculateSourcePath.js';
8+
import { getLastGitCommitDate } from '../../util/getLastGitCommitDate.js';
89

910
export class BuildKeyboardInfo extends BuildActivity {
1011
public get name(): string { return 'Keyboard metadata'; }
@@ -43,13 +44,15 @@ export class BuildKeyboardInfo extends BuildActivity {
4344

4445

4546
const jsFilename = project.resolveOutputFilePath(keyboard, KeymanFileTypes.Source.KeymanKeyboard, KeymanFileTypes.Binary.WebKeyboard);
47+
const lastCommitDate = getLastGitCommitDate(callbacks.path.dirname(project.resolveInputFilePath(metadata)));
4648

4749
const compiler = new KeyboardInfoCompiler(callbacks);
4850
const data = compiler.writeMergedKeyboardInfoFile({
4951
kmpFilename: project.resolveOutputFilePath(kps, KeymanFileTypes.Source.Package, KeymanFileTypes.Binary.Package),
5052
kpsFilename: project.resolveInputFilePath(kps),
5153
jsFilename: fs.existsSync(jsFilename) ? jsFilename : undefined,
52-
sourcePath: calculateSourcePath(infile)
54+
sourcePath: calculateSourcePath(infile),
55+
lastCommitDate
5356
});
5457

5558
if(data == null) {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { execFileSync } from 'child_process';
2+
3+
// RFC3339 pattern, UTC
4+
export const expectedGitDateFormat = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ$/;
5+
6+
/**
7+
* Returns the date and time of the last commit from git for the passed in path
8+
* @param path Path for which to retrieve the last commit message
9+
* @returns string, in RFC3339, 'YYYY-MM-DDThh:nn:ssZ'
10+
*/
11+
export function getLastGitCommitDate(path: string): string {
12+
// TZ=UTC0 git log -1 --no-merges --date=format:%Y-%m-%dT%H:%M:%SZ --format=%ad
13+
let result = null;
14+
15+
try {
16+
result = execFileSync('git', [
17+
'log', // git log
18+
'-1', // one commit only
19+
'--no-merges', // we're only interested in 'real' commits
20+
'--date=format:%Y-%m-%dT%H:%M:%SZ', // format the date in our expected RFC3339 format
21+
'--format=%ad' // emit only the commit date
22+
], {
23+
env: { ...process.env, TZ: 'TZ0' }, // use UTC timezone, not local
24+
encoding: 'utf-8', // force a string result rather than Buffer
25+
windowsHide: true, // on windows, we may need this to suppress a console window popup
26+
cwd: path, // path to run git from
27+
stdio: ['pipe', 'pipe', 'pipe'] // all output via pipe, so we don't get git errors on console
28+
});
29+
} catch (e) {
30+
// If git is not available, or the file is not in-repo, then it is probably
31+
// fine to just silently return null, as the only machines where this is
32+
// critical are the CI machines where we build and deploy .keyboard_info
33+
// files, and where git will always be available. It would be possible to
34+
// have this raise an error in CI environments, but the chance of error
35+
// seems low.
36+
return null;
37+
}
38+
39+
result = result.trim();
40+
41+
// We'll only return the result if it walks like a date, swims like a date,
42+
// and quacks like a date.
43+
if (!result.match(expectedGitDateFormat)) {
44+
return null;
45+
}
46+
47+
return result;
48+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { assert } from 'chai';
2+
import 'mocha';
3+
import { makePathToFixture } from './helpers/index.js';
4+
import { expectedGitDateFormat, getLastGitCommitDate } from '../src/util/getLastGitCommitDate.js';
5+
6+
describe('getLastGitCommitDate', function () {
7+
it('should return a valid date for a folder in this repo', async function() {
8+
const path = makePathToFixture('.');
9+
const date = getLastGitCommitDate(path);
10+
assert.match(date, expectedGitDateFormat);
11+
});
12+
13+
it('should return null for a folder outside the repo', async function() {
14+
const date = getLastGitCommitDate('/');
15+
assert.isNull(date);
16+
});
17+
});

0 commit comments

Comments
 (0)