Skip to content

Commit 9dd28e7

Browse files
feat: Add support for a dry run option for ci environments #188 (#288)
Closes #188 Closes #169 This introduces a dry run option which rather than writing changes to the files instead returns an exit code of 1. This exit code can then be used in ci environments to fail a release/build due to out of date documentation. Note this corrects the check to see if a TOC needs to be transformed as indicated in #169 and as such that pr should be closed. Co-authored-by: Andrew Smith <[email protected]>
1 parent dfa656b commit 9dd28e7

File tree

6 files changed

+109
-11
lines changed

6 files changed

+109
-11
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ by github or other sites via a command line flag.
2020
- [Specifying a custom TOC title](#specifying-a-custom-toc-title)
2121
- [Specifying a minimum heading level for TOC entries](#specifying-a-minimum-heading-level-for-toc-entries)
2222
- [Specifying a maximum heading level for TOC entries](#specifying-a-maximum-heading-level-for-toc-entries)
23+
- [Performing a dry run](#performing-a-dry-run)
2324
- [Printing to stdout](#printing-to-stdout)
2425
- [Only update existing ToC](#only-update-existing-toc)
2526
- [Usage as a `git` hook](#usage-as-a-git-hook)
@@ -140,6 +141,11 @@ By default,
140141
- no limit is placed on Markdown-formatted headings,
141142
- whereas headings from embedded HTML are limited to 4 levels.
142143

144+
### Performing a dry run
145+
146+
Use the `--dryrun` option to not write changes to files but instead return an exit code of 1 to indicates files are out of date and should be updated.
147+
This is useful CI environments where you want to check if your docs are up to date as part of your build process.
148+
143149
### Printing to stdout
144150

145151
You can print to stdout by using the `-s` or `--stdout` option.

doctoc.js

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function cleanPath(path) {
1515
return homeExpanded;
1616
}
1717

18-
function transformAndSave(files, mode, maxHeaderLevel, minHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly) {
18+
function transformAndSave(files, mode, maxHeaderLevel, minHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly, dryRun) {
1919
if (processAll) {
2020
console.log('--all flag is enabled. Including headers before the TOC location.')
2121
}
@@ -44,17 +44,28 @@ function transformAndSave(files, mode, maxHeaderLevel, minHeaderLevel, title, no
4444
}
4545

4646
unchanged.forEach(function (x) {
47-
console.log('"%s" is up to date', x.path);
47+
if (stdOut) {
48+
console.log('==================\n\n"%s" is up to date', x.path)
49+
}
50+
else {
51+
console.log('"%s" is up to date', x.path);
52+
}
4853
});
4954

5055
changed.forEach(function (x) {
5156
if (stdOut) {
5257
console.log('==================\n\n"%s" should be updated', x.path)
53-
} else {
58+
} else if (dryRun) {
59+
console.log('"%s" should be updated but wasn\'t due to dry run.', x.path);
60+
}
61+
else {
5462
console.log('"%s" will be updated', x.path);
5563
fs.writeFileSync(x.path, x.data, 'utf8');
5664
}
5765
});
66+
if (dryRun && changed.length > 0) {
67+
process.exitCode = 1;
68+
}
5869
}
5970

6071
function printUsageAndExit(isErr) {
@@ -82,7 +93,7 @@ var modes = {
8293
var mode = modes['github'];
8394

8495
var argv = minimist(process.argv.slice(2)
85-
, { boolean: [ 'h', 'help', 'T', 'notitle', 's', 'stdout', 'all' , 'u', 'update-only'].concat(Object.keys(modes))
96+
, { boolean: [ 'h', 'help', 'T', 'notitle', 's', 'stdout', 'all' , 'u', 'update-only', 'd', 'dryrun'].concat(Object.keys(modes))
8697
, string: [ 'title', 't', 'maxlevel', 'm', 'minlevel', 'n', 'entryprefix' ]
8798
, unknown: function(a) { return (a[0] == '-' ? (console.error('Unknown option(s): ' + a), printUsageAndExit(true)) : true); }
8899
});
@@ -103,6 +114,7 @@ var entryPrefix = argv.entryprefix || '-';
103114
var processAll = argv.all;
104115
var stdOut = argv.s || argv.stdout
105116
var updateOnly = argv.u || argv['update-only']
117+
var dryRun = argv.d || argv.dryrun || false;
106118

107119
var maxHeaderLevel = argv.m || argv.maxlevel;
108120
if (maxHeaderLevel && isNaN(maxHeaderLevel)) { console.error('Max. heading level specified is not a number: ' + maxHeaderLevel), printUsageAndExit(true); }
@@ -126,9 +138,14 @@ for (var i = 0; i < argv._.length; i++) {
126138
files = [{ path: target }];
127139
}
128140

129-
transformAndSave(files, mode, maxHeaderLevel, minHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly);
141+
transformAndSave(files, mode, maxHeaderLevel, minHeaderLevel, title, notitle, entryPrefix, processAll, stdOut, updateOnly, dryRun);
130142

131-
console.log('\nEverything is OK.');
143+
if (dryRun && process.exitCode === 1) {
144+
console.log('\nDocumentation tables of contents are out of date.');
145+
}
146+
else {
147+
console.log('\nEverything is OK.');
148+
}
132149
}
133150

134151
module.exports.transform = transform;

lib/transform.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,11 @@ exports = module.exports = function transform(content, mode, maxHeaderLevel, min
164164

165165
var wrappedToc = start + '\n' + toc + '\n' + end;
166166

167-
if (currentToc === toc) return { transformed: false };
168-
169-
var data = updateSection(lines.join('\n'), wrappedToc, matchesStart, matchesEnd, true);
170-
return { transformed : true, data : data, toc: toc, wrappedToc: wrappedToc };
167+
var data;
168+
if (currentToc != wrappedToc){
169+
data = updateSection(lines.join('\n'), wrappedToc, matchesStart, matchesEnd, true);
170+
}
171+
return { transformed : currentToc != wrappedToc, data : data, toc: toc, wrappedToc: wrappedToc };
171172
};
172173

173174
exports.start = start;
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Hello, world!
2+
3+
README to test doctoc with user-specified titles.
4+
5+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
6+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
7+
## Table of Contents
8+
9+
- [Installation](#installation)
10+
- [API](#api)
11+
- [License](#license)
12+
13+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
14+
15+
16+
## Installation
17+
## API
18+
## Contributing
19+
## License

test/fixtures/stdout.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ DocToccing single file "test/fixtures/readme-with-custom-title.md" for github.co
1111

1212
==================
1313

14-
"test/fixtures/readme-with-custom-title.md" should be updated
14+
"test/fixtures/readme-with-custom-title.md" is up to date
1515

1616
Everything is OK.

test/transform-dryrun.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
'use strict';
2+
/*jshint asi: true */
3+
4+
var test = require('tap').test,
5+
fs = require('fs'),
6+
exec = require("child_process").exec;
7+
8+
test('\nshould exit with a error code due to --dryrun option', function (t) {
9+
10+
exec('node doctoc.js test/fixtures/readme-with-out_of_date_toc.md --dryrun', function (error, stdout, stderr) {
11+
if (error) {
12+
t.deepEqual(error.code, 1, 'process exited with error code 1 as expected');
13+
t.end('process did have an error');
14+
} else {
15+
t.fail('process did not produce an error: ' + error);
16+
t.end();
17+
}
18+
})
19+
})
20+
21+
test('\nshould exit with no error code with --dryrun option', function (t) {
22+
23+
exec('node doctoc.js test/fixtures/readme-with-custom-title.md --dryrun', function (error, stdout, stderr) {
24+
if (error) {
25+
t.fail('process produced an unexpected error: ' + error);
26+
t.end()
27+
} else {
28+
t.end('process did not have an error');
29+
}
30+
})
31+
})
32+
33+
test('\nshould exit no error code due to no --dryrun option for out of date toc', function (t) {
34+
35+
exec('node doctoc.js test/fixtures/readme-with-out_of_date_toc.md --stdout', function (error, stdout, stderr) {
36+
if (error) {
37+
t.fail('process produced an unexpected error: ' + error);
38+
t.end()
39+
} else {
40+
t.end('process did not have an error');
41+
}
42+
})
43+
})
44+
45+
test('\nshould exit with no error code', function (t) {
46+
47+
exec('node doctoc.js test/fixtures/readme-with-custom-title.md', function (error, stdout, stderr) {
48+
if (error) {
49+
t.fail('process produced an unexpected error: ' + error);
50+
t.end()
51+
} else {
52+
t.end('process did not have an error');
53+
}
54+
})
55+
})

0 commit comments

Comments
 (0)