Skip to content

Commit 67e04d0

Browse files
authored
Feat: recover the complete changelog.md (#10)
* Style: add red color to warnings * Feat: recover the complete changelog.md * Test: for new feature complete changelog.md * Feat: cli support for recover complete changelog.md * Chore: add new dependecies * Docs: add description for new feat * Fix: problem with babel * CI: tmp fix, downgrade to [email protected] on windows * Refactor: remove unnecessary try catch * Test: update for better coverage * Test: remove Object.keys * Test: fix tests * Refactor: create changelog parts * Test: update for new feat and update to fs-extra * Style: remove index
1 parent 82d1fcc commit 67e04d0

File tree

12 files changed

+698
-439
lines changed

12 files changed

+698
-439
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,14 @@ So there are a few tasks `semantic-git-release-cli` will do for you:
4949
- updates the `version` in `package.json`
5050
- creates or updates the `CHANGELOG.md`
5151
- commits and tags the new `version`
52+
53+
## Recover the CHANGELOG.md
54+
55+
If you are start to use `semantic-git-release-cli` but already released (and tagged) versions. Simply use the recover mode.
56+
57+
```sh
58+
# generates the complete CHANGELOG.md
59+
sgr --recover # or short `sgr -r`
60+
# generates the complete CHANGELOG.md and creates a backup of the current CHANGELOG.md in .sgr_backup
61+
sgr -r backup # or short `sgr -r b`
62+
```

appveyor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ install:
1111
- ps: Install-Product node $env:nodejs_version
1212
# install modules
1313
- npm config set loglevel warn
14-
- npm i -g npm
14+
- npm i -g npm@5.3
1515
- npm i
1616

1717
# Post-install test scripts.

lib/cli.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ import getLatestVersion from './helpers/getLatestVersion';
1515
import pkg from '../package.json';
1616
import questions from './questions';
1717
import tasks from './tasks';
18+
import recoverTasks from './recover-tasks';
1819

1920
const argv = yargs
2021
.usage('Usage: $0')
2122
.alias('v', 'version')
2223
.describe('v', 'Version number')
2324
.help('h')
2425
.alias('h', 'help')
26+
.alias('r', 'recover')
27+
.describe('r', 'Recover the complete CHANGELOG.md')
2528
.argv;
2629

2730
updateNotifier({ pkg }).notify();
@@ -40,7 +43,7 @@ const cli = () => {
4043
} else if (!isAdded(cwd) && commits.length === 0) {
4144
return console.warn(chalk.red('Error: no changes... try to git add <files>'));
4245
} else if (commits.length === 0) {
43-
return console.warn(chalk.red('Error: no commits yet... try to git commit -m <message>'));
46+
return console.warn(chalk.red('Error: no commits yet... try to git commit -m <message>'));
4447
} else if (!getGitRemotes(cwd)) {
4548
return console.warn(chalk.red('Error: it seems you do not have a remote repository set... try to git remote add origin <remote-url>'));
4649
} else if (latestVersion === '') {
@@ -53,18 +56,25 @@ const cli = () => {
5356
if (answers.ownVersion) {
5457
return tasks(commits, answers.ownVersion)
5558
.run()
56-
.catch(() => console.warn('Error: whoops, try to solve the problem mentioned above...'));
59+
.catch(() => console.warn(chalk.red('Error: whoops, try to solve the problem mentioned above...')));
5760
}
5861

5962
return tasks(commits, answers.version)
6063
.run()
61-
.catch(() => console.warn('Error: whoops, try to solve the problem mentioned above...'));
64+
.catch(() => console.warn(chalk.red('Error: whoops, try to solve the problem mentioned above...')));
6265
})
63-
.catch((err) => console.warn(err));
66+
.catch((err) => console.warn(chalk.red(err)));
6467
};
6568

6669
if (argv.v) {
6770
console.info(`v${pkg.version}`);
71+
} else if (argv.recover) {
72+
if (argv.recover === 'b' || argv.recover === 'backup') {
73+
recoverTasks(true).run();
74+
} else {
75+
recoverTasks(false).run();
76+
}
6877
} else {
6978
cli();
7079
}
80+

lib/helpers/changelogParts.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import moment from 'moment';
2+
import gitCommitInfo from 'git-commit-info';
3+
4+
const header = (version, date = '') => {
5+
if (date === '') {
6+
return `${version} - ${moment().format('MMMM, DD YYYY')}`;
7+
}
8+
9+
return `${version} - ${moment(date, 'ddd MMM D HH:mm:ss YYYY Z').format('MMMM, DD YYYY')}`;
10+
};
11+
12+
const oneCommit = (commitInfo) => (
13+
`* ${commitInfo.shortHash} ${commitInfo.message.split('\n')[0]} (${commitInfo.author})`
14+
);
15+
16+
const body = (commits, version) => {
17+
const cwd = process.cwd();
18+
let changelogData = '';
19+
20+
commits.forEach((commit, i) => {
21+
const commitInfo = gitCommitInfo({ commit, cwd });
22+
23+
/* istanbul ignore next */
24+
if (!commitInfo.shortHash ||
25+
!commitInfo.author ||
26+
!commitInfo.message ||
27+
commitInfo.message.split('\n')[0] === version) {
28+
return;
29+
}
30+
31+
changelogData = `${changelogData} ${oneCommit(commitInfo)}\n`;
32+
33+
if (commits.length - 1 === i) {
34+
changelogData = `${changelogData}\n`;
35+
}
36+
});
37+
38+
return changelogData;
39+
};
40+
41+
export {
42+
header,
43+
body,
44+
};
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import fs from 'fs-extra';
2+
import gitCommitInfo from 'git-commit-info';
3+
import getCommitRange from 'git-commit-range';
4+
import moment from 'moment';
5+
import path from 'path';
6+
import prependFile from 'prepend-file';
7+
import taggedCommits from 'tagged-git-commits';
8+
import {
9+
header,
10+
body,
11+
} from './changelogParts';
12+
13+
const writeBackup = () => {
14+
const cwd = process.cwd();
15+
16+
fs.renameSync(
17+
path.join(cwd, 'CHANGELOG.md'),
18+
path.join(cwd, '.sgr_backup', `CHANGELOG.${moment().unix()}.bak.md`),
19+
);
20+
};
21+
22+
const backupChangelog = () => {
23+
const cwd = process.cwd();
24+
25+
if (fs.existsSync(path.join(cwd, '.sgr_backup'))) {
26+
return writeBackup();
27+
}
28+
29+
fs.mkdirSync(path.join(cwd, '.sgr_backup'));
30+
31+
return writeBackup();
32+
};
33+
34+
const getAllTags = () => {
35+
const cwd = process.cwd();
36+
const tags = taggedCommits({
37+
path: cwd,
38+
lookBehind: Number.POSITIVE_INFINITY,
39+
});
40+
41+
return tags;
42+
};
43+
44+
const writeToFile = (tags, exists, backup) => {
45+
const cwd = process.cwd();
46+
let changelogData = '';
47+
let commits = [];
48+
let tagDate = '';
49+
50+
if (exists && backup) {
51+
backupChangelog();
52+
} else if (exists) {
53+
fs.truncateSync(path.join(cwd, 'CHANGELOG.md'), 0);
54+
}
55+
56+
tags.forEach((tag, idx) => {
57+
if (idx === 0) {
58+
commits = getCommitRange({
59+
path: cwd,
60+
});
61+
commits = getCommitRange({
62+
path: cwd,
63+
from: commits[commits.length - 1],
64+
to: tag.hash,
65+
});
66+
} else {
67+
commits = getCommitRange({
68+
path: cwd,
69+
from: tags[idx - 1].hash,
70+
to: tag.hash,
71+
});
72+
}
73+
74+
tagDate = gitCommitInfo({
75+
cwd,
76+
commit: tag.hash,
77+
}).date;
78+
79+
const version = tag.version.slice(1, tag.version.length);
80+
81+
changelogData = `${header(version, tagDate)}\n\n${body(commits, version)}`;
82+
83+
prependFile.sync(path.join(cwd, 'CHANGELOG.md'), changelogData);
84+
});
85+
};
86+
87+
const generateCompleteChangelog = (backup) => {
88+
const cwd = process.cwd();
89+
90+
try {
91+
const exists = fs.existsSync(path.join(cwd, 'CHANGELOG.md'));
92+
93+
if (!exists) {
94+
fs.writeFileSync(path.join(cwd, 'CHANGELOG.md'), '');
95+
}
96+
97+
return writeToFile(getAllTags(), exists, backup);
98+
} catch (err) {
99+
return false;
100+
}
101+
};
102+
103+
export default generateCompleteChangelog;

lib/helpers/updateChangelog.js

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,28 @@
1-
import fs from 'fs';
2-
import gitCommitInfo from 'git-commit-info';
3-
import moment from 'moment';
1+
import fs from 'fs-extra';
42
import path from 'path';
3+
import prependFile from 'prepend-file';
4+
import {
5+
header,
6+
body,
7+
} from './changelogParts';
58

6-
const writeToFile = (commits = [], version, exists) => {
9+
const writeToFile = (commits = [], version) => {
710
const cwd = process.cwd();
11+
const changelogData = `${header(version)}\n\n${body(commits, version)}`;
812

9-
let changelogData;
10-
11-
if (exists) {
12-
const changelog = fs.readFileSync(path.join(cwd, 'CHANGELOG.md'), 'utf8');
13-
changelogData = `\n${changelog}`;
14-
} else {
15-
changelogData = '';
16-
}
17-
18-
const stream = fs.createWriteStream(path.join(cwd, 'CHANGELOG.md'), 'utf8');
19-
20-
stream.write(`${version} - ${moment().format('MMMM, DD YYYY')}\n\n`);
21-
22-
commits.forEach((commithash) => {
23-
const commitInfo = gitCommitInfo({ commit: commithash, cwd });
24-
25-
stream.write(`* ${commitInfo.shortHash} ${commitInfo.message.split('\n')[0]} (${commitInfo.author})\n`);
26-
});
27-
28-
stream.write(changelogData);
29-
30-
return stream.end();
13+
prependFile.sync(path.join(cwd, 'CHANGELOG.md'), changelogData);
3114
};
3215

3316
const updateChangelog = (commits = [], version) => {
34-
try {
35-
const cwd = process.cwd();
17+
const cwd = process.cwd();
3618

19+
try {
3720
const exists = fs.existsSync(path.join(cwd, 'CHANGELOG.md'));
3821

22+
if (!exists) {
23+
fs.writeFileSync(path.join(cwd, 'CHANGELOG.md'), '');
24+
}
25+
3926
return writeToFile(commits, version, exists);
4027
} catch (err) {
4128
return false;

lib/recover-tasks/index.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import Listr from 'listr';
2+
3+
import generateCompleteChangelog from '../helpers/generateCompleteChangelog';
4+
5+
const recoverTasks = (backup) => (
6+
new Listr([
7+
{
8+
title: 'Recover the complete CHANGELOG.md',
9+
task: () => {
10+
generateCompleteChangelog(backup);
11+
},
12+
},
13+
])
14+
);
15+
16+
export default recoverTasks;

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@
5555
"homepage": "https://github.com/aichbauer/node-semantic-git-release-cli#readme",
5656
"devDependencies": {
5757
"ava": "^0.18.2",
58-
"babel-cli": "^6.24.0",
5958
"babel-polyfill": "^6.23.0",
6059
"babel-preset-env": "^1.2.1",
6160
"coveralls": "^2.12.0",
@@ -66,7 +65,9 @@
6665
"nyc": "^10.1.2"
6766
},
6867
"dependencies": {
68+
"babel-cli": "^6.26.0",
6969
"chalk": "^2.0.1",
70+
"count-git-tags": "^1.0.0",
7071
"execa": "^0.6.3",
7172
"fs-extra": "^3.0.1",
7273
"get-git-remotes": "^1.0.1",
@@ -78,6 +79,7 @@
7879
"is-git-repository": "^1.1.1",
7980
"listr": "^0.12.0",
8081
"moment": "^2.18.1",
82+
"prepend-file": "^1.3.1",
8183
"semver": "^5.3.0",
8284
"tagged-git-commits": "^1.0.0",
8385
"update-notifier": "^2.1.0",

0 commit comments

Comments
 (0)