1 | /*
|
2 | * Licensed under the Apache License, Version 2.0 (the "License");
|
3 | * you may not use this file except in compliance with the License.
|
4 | * You may obtain a copy of the License at
|
5 | *
|
6 | * http://www.apache.org/licenses/LICENSE-2.0
|
7 | *
|
8 | * Unless required by applicable law or agreed to in writing, software
|
9 | * distributed under the License is distributed on an "AS IS" BASIS,
|
10 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11 | * See the License for the specific language governing permissions and
|
12 | * limitations under the License.
|
13 | */
|
14 |
|
15 | ;
|
16 |
|
17 | const crypto = require('crypto');
|
18 | const semver = require('semver');
|
19 |
|
20 | /**
|
21 | * Checks that a change log file takes into account
|
22 | * the signature of a public API (tracks API breakage)
|
23 | * and that the version number in package.json is in sync
|
24 | * with the contents of the changelog.
|
25 | * @private
|
26 | * @class
|
27 | * @memberof module:concerto
|
28 | */
|
29 | class VersionChecker {
|
30 |
|
31 | /**
|
32 | * @param {string} changelog - the text of a changelog file
|
33 | * @param {string} publicApi - the text of a public API description
|
34 | * @param {string} packageJson - the text of a package.json file
|
35 | * @returns {boolean} true if the version check passes
|
36 | * @throws {Error} if there is an issue with the version check
|
37 | */
|
38 | static check(changelog, publicApi, packageJson) {
|
39 | const changelogLines = changelog.split('\n');
|
40 | const digest = VersionChecker.getDigest(publicApi);
|
41 | let result = false;
|
42 |
|
43 | for (let n = 0; n < changelogLines.length; n++) {
|
44 | const line = changelogLines[n];
|
45 |
|
46 | if (!line.startsWith('#')) {
|
47 | // find the first instance of 'Version'
|
48 | const versionIndex = line.indexOf('Version');
|
49 | if (versionIndex >= 0) {
|
50 | // find the version number
|
51 | const openBraceIndex = line.indexOf('{', versionIndex);
|
52 |
|
53 | if (openBraceIndex < 0) {
|
54 | throw new Error('Invalid changelog, failed to find { in line ' + line);
|
55 | }
|
56 |
|
57 | const version = line.substring(versionIndex + 'Version'.length, openBraceIndex).trim();
|
58 |
|
59 | // check the version in package.json is up to date
|
60 | const packageObj = JSON.parse(packageJson);
|
61 |
|
62 | if (!semver.lte(version, packageObj.version)) {
|
63 | throw new Error(`The version in the changelog file "${version}" is not less than or equal to the version in package.json "${packageObj.version}".`);
|
64 | }
|
65 |
|
66 | // get MD5
|
67 | const closeBraceIndex = line.indexOf('}', openBraceIndex);
|
68 |
|
69 | if (closeBraceIndex < 0) {
|
70 | throw new Error('Invalid changelog, failed to find } in line ' + line);
|
71 | }
|
72 |
|
73 | const md5 = line.substring(openBraceIndex + 1, closeBraceIndex).trim();
|
74 |
|
75 | if (digest !== md5) {
|
76 | throw new Error('Computed public API digest did not match the digest in the changelog for the most recent version. ' +
|
77 | 'Increment the version number and add a new entry to the changelog (explaining your public API change) using the digest ' + digest +
|
78 | '. Run \'git diff api.txt\' to understand the pubic API changes.');
|
79 | }
|
80 |
|
81 | // we're done here...
|
82 | result = true;
|
83 | break;
|
84 | }
|
85 | }
|
86 |
|
87 | }
|
88 | if (!result) {
|
89 | throw new Error('Did not find any version in changelog');
|
90 | }
|
91 | else {
|
92 | console.log('SUCCESS: validated public API against package.json and changelog.txt.');
|
93 | }
|
94 |
|
95 | return true;
|
96 | }
|
97 |
|
98 | /**
|
99 | * Gets the digest (hash) for an input string
|
100 | * @param {string} data - the data to hash
|
101 | * @returns {string} the hash in hex format
|
102 | */
|
103 | static getDigest(data) {
|
104 | return crypto.createHash('md5').update(data).digest('hex');
|
105 | }
|
106 | }
|
107 |
|
108 | module.exports = VersionChecker;
|