Skip to content

Commit 58ee2fe

Browse files
committed
build: apply instructions from git notes when parsing commits
1 parent 854b793 commit 58ee2fe

File tree

4 files changed

+312
-6
lines changed

4 files changed

+312
-6
lines changed

lib/node_modules/@stdlib/_tools/changelog/parse-commits/lib/commits.js

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,18 @@ var resolve = require( 'path' ).resolve;
2424
var join = require( 'path' ).join;
2525
var spawn = require( 'child_process' ).spawnSync; // eslint-disable-line node/no-sync
2626
var logger = require( 'debug' );
27+
var parse = require( 'yaml' ).parse;
28+
var substringAfter = require( '@stdlib/string/substring-after' );
2729
var replace = require( '@stdlib/string/replace' );
2830
var trim = require( '@stdlib/string/trim' );
31+
var filterFiles = require( './filter_files.js' );
2932

3033

3134
// VARIABLES //
3235

3336
var debug = logger( 'changelog:parse-commits' );
3437
var GIT_COMMIT_SEP = '^---^';
38+
var YAML_SEP = '---';
3539

3640

3741
// FUNCTIONS //
@@ -51,6 +55,19 @@ function escapeDoubleQuotes( str ) {
5155
return replace( str, '"', '\\"' );
5256
}
5357

58+
/**
59+
* Extracts a YAML block surrounded by delimiters from a string.
60+
*
61+
* @private
62+
* @param {string} str - input string
63+
* @returns {string} extracted YAML block
64+
*/
65+
function extractYAMLBlock( str ) {
66+
var start = str.indexOf( YAML_SEP ) + YAML_SEP.length;
67+
var end = str.indexOf( YAML_SEP, start );
68+
return str.substring( start, end );
69+
}
70+
5471

5572
// MAIN //
5673

@@ -67,11 +84,19 @@ function escapeDoubleQuotes( str ) {
6784
*/
6885
function extractCommits( dir ) {
6986
var commits;
87+
var author;
88+
var parsed;
89+
var files;
7090
var lines;
91+
var notes;
7192
var parts;
7293
var args;
94+
var hash;
95+
var date;
7396
var opts;
97+
var yaml;
7498
var cmd;
99+
var msg;
75100
var out;
76101
var i;
77102

@@ -98,12 +123,36 @@ function extractCommits( dir ) {
98123
continue;
99124
}
100125
parts = lines[ i ].split( '|' );
126+
hash = trim( parts[ 0 ] );
127+
date = parts[ 1 ];
128+
author = parts[ 2 ];
129+
130+
msg = trim( escapeDoubleQuotes( parts[ 3 ] ) );
131+
notes = parts[ 4 ];
132+
files = trim( parts[ 5 ] ).split( '\n' );
133+
if ( notes ) {
134+
// Extract YAML block from git notes and apply any specified transformations:
135+
yaml = extractYAMLBlock( notes );
136+
parsed = parse( yaml );
137+
switch ( parsed.type ) {
138+
case 'amend-message':
139+
// Replace the commit message with the message from the notes:
140+
msg = substringAfter( notes, yaml + YAML_SEP + '\n' );
141+
break;
142+
case 'filter-packages':
143+
// Filter out files which are part of the excluded packages:
144+
files = filterFiles( files, parsed.exclude );
145+
break;
146+
default:
147+
break;
148+
}
149+
}
101150
commits.push({
102-
'hash': trim( parts[ 0 ] ),
103-
'date': parts[ 1 ],
104-
'author': parts[ 2 ],
105-
'message': trim( escapeDoubleQuotes( parts[ 3 ] ) ),
106-
'files': trim( parts[ 4 ] ).split( '\n' )
151+
'hash': hash,
152+
'date': date,
153+
'author': author,
154+
'message': msg,
155+
'files': files
107156
});
108157
}
109158
return commits;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2024 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var contains = require( '@stdlib/array/base/assert/contains' );
24+
var replace = require( '@stdlib/string/base/replace' );
25+
var filter = require( '@stdlib/array/base/filter' );
26+
27+
28+
// VARIABLES //
29+
30+
var RE_STDLIB_PREFIX = /^lib\/node_modules\/@stdlib\//;
31+
32+
33+
// FUNCTIONS //
34+
35+
/**
36+
* Extracts a package path from a file path.
37+
*
38+
* @private
39+
* @param {string} filepath - input file path to process
40+
* @returns {string} package path
41+
*
42+
* @example
43+
* var path = 'lib/node_modules/@stdlib/complex/float64/base/assert/is-equal/lib/main.js';
44+
* var result = extractPackage( path );
45+
* // returns 'complex/float64/base/assert/is-equal'
46+
*/
47+
function extractPackage( filepath ) {
48+
var parts;
49+
var idx;
50+
var i;
51+
52+
filepath = replace( filepath, RE_STDLIB_PREFIX, '' );
53+
parts = filepath.split( '/' );
54+
55+
// Remove the last part as it's a file name:
56+
parts.pop();
57+
58+
// Find the index of the first occurrence of certain package subdirectories:
59+
idx = -1;
60+
for ( i = 0; i < parts.length; i++ ) {
61+
if (
62+
parts[ i ] === 'bin' ||
63+
parts[ i ] === 'benchmark' ||
64+
parts[ i ] === 'data' ||
65+
parts[ i ] === 'docs' ||
66+
parts[ i ] === 'etc' ||
67+
parts[ i ] === 'examples' ||
68+
parts[ i ] === 'include' ||
69+
parts[ i ] === 'lib' ||
70+
parts[ i ] === 'src' ||
71+
parts[ i ] === 'test'
72+
) {
73+
idx = i;
74+
break;
75+
}
76+
}
77+
78+
// If we found a package subdirectory, return the package path up to that point:
79+
if ( idx !== -1 ) {
80+
parts.length = idx;
81+
}
82+
return parts.join( '/' );
83+
}
84+
85+
86+
// MAIN //
87+
88+
/**
89+
* Filters files based on a list of packages to exclude.
90+
*
91+
* @private
92+
* @param {Array} files - list of files
93+
* @param {Array} exclude - list of packages to exclude
94+
* @returns {Array} filtered files
95+
*
96+
* @example
97+
* var files = [
98+
* 'lib/node_modules/@stdlib/stats/base/dists/normal/pdf/test/test.js',
99+
* 'lib/node_modules/@stdlib/utils/copy/examples/index.js'
100+
* ];
101+
* var exclude = [ 'stats/base/dists/normal/pdf' ];
102+
* var out = filterFiles( files, exclude );
103+
* // returns [ 'lib/node_modules/@stdlib/utils/copy/examples/index.js' ]
104+
*/
105+
function filterFiles( files, exclude ) {
106+
return filter( files, predicate );
107+
108+
/**
109+
* Predicate function.
110+
*
111+
* @private
112+
* @param {string} file - file path
113+
* @returns {boolean} boolean indicating whether to include a file
114+
*/
115+
function predicate( file ) {
116+
return !contains( exclude, extractPackage( file ) );
117+
}
118+
}
119+
120+
121+
// EXPORTS //
122+
123+
module.exports = filterFiles;

lib/node_modules/@stdlib/_tools/changelog/parse-commits/scripts/commits.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,4 @@ if [ -z "$GIT_COMMIT_SEP" ]; then
3838
GIT_COMMIT_SEP="^---^"; # Default separator
3939
fi
4040

41-
git log --name-only --no-merges --pretty=format:"%H|%ad|%aN <%aE>|%B|" "$1" | awk -v sep="$GIT_COMMIT_SEP" '/^$/{p=1;next} /^[0-9a-f]{40}\|/{if (p==1) print sep; p=0} {print}';
41+
git log --name-only --no-merges --notes --pretty=format:"%H|%ad|%aN <%aE>|%B|%N|" "$1" | awk -v sep="$GIT_COMMIT_SEP" '/^$/{p=1;next} /^[0-9a-f]{40}\|/{if (p==1) print sep; p=0} {print}';
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* @license Apache-2.0
3+
*
4+
* Copyright (c) 2024 The Stdlib Authors.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
'use strict';
20+
21+
// MODULES //
22+
23+
var tape = require( 'tape' );
24+
var filterFiles = require( './../lib/filter_files.js' );
25+
26+
27+
// TESTS //
28+
29+
tape( 'main export is a function', function test( t ) {
30+
t.ok( true, __filename );
31+
t.equal( typeof filterFiles, 'function', 'main export is a function' );
32+
t.end();
33+
});
34+
35+
tape( 'the function filters files based on excluded packages', function test( t ) {
36+
var expected;
37+
var exclude;
38+
var result;
39+
var files;
40+
41+
files = [
42+
'lib/node_modules/@stdlib/math/base/special/abs/lib/index.js',
43+
'lib/node_modules/@stdlib/stats/base/dists/normal/pdf/lib/index.js',
44+
'lib/node_modules/@stdlib/utils/copy/lib/index.js'
45+
];
46+
exclude = [ 'stats/base/dists/normal/pdf' ];
47+
expected = [
48+
'lib/node_modules/@stdlib/math/base/special/abs/lib/index.js',
49+
'lib/node_modules/@stdlib/utils/copy/lib/index.js'
50+
];
51+
52+
result = filterFiles( files, exclude );
53+
t.deepEqual( result, expected, 'returns expected value' );
54+
t.end();
55+
});
56+
57+
tape( 'the function handles empty exclude list', function test( t ) {
58+
var expected;
59+
var exclude;
60+
var result;
61+
var files;
62+
63+
files = [
64+
'lib/node_modules/@stdlib/math/base/special/abs/lib/index.js',
65+
'lib/node_modules/@stdlib/stats/base/dists/normal/pdf/lib/index.js'
66+
];
67+
exclude = [];
68+
expected = [
69+
'lib/node_modules/@stdlib/math/base/special/abs/lib/index.js',
70+
'lib/node_modules/@stdlib/stats/base/dists/normal/pdf/lib/index.js'
71+
];
72+
73+
result = filterFiles( files, exclude );
74+
t.deepEqual( result, expected, 'returns expected value' );
75+
t.end();
76+
});
77+
78+
tape( 'the function handles empty file list', function test( t ) {
79+
var expected;
80+
var exclude;
81+
var result;
82+
var files;
83+
84+
files = [];
85+
exclude = [ 'math/base/special/abs' ];
86+
expected = [];
87+
88+
result = filterFiles( files, exclude );
89+
t.deepEqual( result, expected, 'returns expected value' );
90+
t.end();
91+
});
92+
93+
tape( 'the function correctly extracts package paths', function test( t ) {
94+
var expected;
95+
var exclude;
96+
var result;
97+
var files;
98+
99+
files = [
100+
'lib/node_modules/@stdlib/complex/float64/base/assert/is-equal/lib/main.js',
101+
'lib/node_modules/@stdlib/math/base/special/abs/benchmark/benchmark.js',
102+
'lib/node_modules/@stdlib/stats/base/dists/normal/pdf/test/test.js',
103+
'lib/node_modules/@stdlib/utils/copy/examples/index.js'
104+
];
105+
exclude = [ 'complex/float64/base/assert/is-equal', 'stats/base/dists/normal/pdf' ];
106+
expected = [
107+
'lib/node_modules/@stdlib/math/base/special/abs/benchmark/benchmark.js',
108+
'lib/node_modules/@stdlib/utils/copy/examples/index.js'
109+
];
110+
111+
result = filterFiles( files, exclude );
112+
t.deepEqual( result, expected, 'returns expected value' );
113+
t.end();
114+
});
115+
116+
tape( 'the function correctly handles files without package subdirectories', function test( t ) {
117+
var expected;
118+
var exclude;
119+
var result;
120+
var files;
121+
122+
files = [
123+
'lib/node_modules/@stdlib/math/package.json',
124+
'lib/node_modules/@stdlib/stats/package.json'
125+
];
126+
exclude = [ 'stats' ];
127+
expected = [
128+
'lib/node_modules/@stdlib/math/package.json'
129+
];
130+
131+
result = filterFiles( files, exclude );
132+
t.deepEqual( result, expected, 'returns expected value' );
133+
t.end();
134+
});

0 commit comments

Comments
 (0)