Mirror of Awesome Self Hosted
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
7.3 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
5 years ago
8 years ago
8 years ago
8 years ago
  1. // USAGE:
  2. // node test.js -r README.md (Checks whole file)
  3. // node test.js -r README.md -d temp.md (Checks just the diff)
  4. const fs = require('fs');
  5. const chalk = require('chalk');
  6. let licenses = new Set();
  7. let pr = false;
  8. let readme;
  9. let diff;
  10. //Parse the command options and set the pr var
  11. function parseArgs(args) {
  12. if ( args.indexOf('-r', 2) > 0 ) {
  13. readme = fs.readFileSync(args[args.indexOf('-r', 2)+1], 'utf8')
  14. }
  15. if (args.indexOf('-d', 2) > 0) {
  16. pr = true;
  17. diff = fs.readFileSync(args[args.indexOf('-d', 2)+1], 'utf8');
  18. }
  19. if ( pr === true) {
  20. console.log(chalk.blue(`Running on PR. README.md: ${args[args.indexOf('-r', 2)+1]} diff: ${args[args.indexOf('-d', 2)+1]}`))
  21. }
  22. }
  23. // Function to find lines with entries
  24. function entryFilter(md) {
  25. const linepatt = /^\s{0,2}-\s\[.*`/;
  26. return linepatt.test(md);
  27. }
  28. // Function to find lines with licenses
  29. function licenseFilter(md) {
  30. const linepatt = /^- `.*` - .*/;
  31. return linepatt.test(md)
  32. }
  33. // Function to split lines into array
  34. function split(text) {
  35. return text.split(/\r?\n/);
  36. }
  37. // All entries should match this pattern. If matches pattern returns true.
  38. function findPattern(text) {
  39. const patt = /^\s{0,2}-\s\[.*?\]\(.*?\) (`⚠` )?- .{0,249}?\.( \(\[(Demo|Source Code|Clients)\]\([^)\]]*\)(, \[(Source Code|Clients)\]\([^)\]]*\))?(, \[(Source Code|Clients)\]\([^)\]]*\))*\))? \`.*?\` \`.*?\`$/;
  40. if (patt.test(text) === true) {
  41. return true;
  42. }
  43. return false;
  44. }
  45. // Parses SPDX identifiers from list of licenses
  46. function parseLicense(md) {
  47. const patt = /^- `(.*)` - .*/
  48. return patt.exec(md)[1]
  49. }
  50. //Test '- [Name](http://homepage/)'
  51. function testMainLink(text) {
  52. let testA = /(^ {0,2}- \[.*?\]\([^)]*\.[^)]*?\))(?=\ ?\-?\ ?\w)/ // /(^ {0,2}- \[.*?\]\(.*\))(?=.?-? ?\w)/;
  53. const testA1 = /(- \W?\w*\W{0,2}.*?\)?)( .*$)/;
  54. if (!testA.test(text)) {
  55. let a1 = testA1.exec(text)[2];
  56. return chalk.red(text.replace(a1, ''))
  57. }
  58. return chalk.green(testA.exec(text)[1])
  59. }
  60. //Test '`⚠` - Short description, less than 250 characters.'
  61. function testDescription(text) {
  62. const testB = /( - .*\. )(?:(\(?\[?|\`))/;
  63. const testA1 = /(- \W?\w*\W{0,2}.*?\)?)( .*$)/;
  64. const testB2 = /((\(\[|\`).*$)/;
  65. if (!testB.test(text)) {
  66. let b1 = testA1.exec(text)[1];
  67. let b2 = testB2.exec(text)[1];
  68. return chalk.red(text.replace(b1, '').replace(b2, ''))
  69. }
  70. return chalk.green(testB.exec(text)[1])
  71. }
  72. //If present, tests '([Demo](http://url.to/demo), [Source Code](http://url.of/source/code), [Clients](https://url.to/list/of/related/clients-or-apps))'
  73. function testSrcDemCli(text) {
  74. let testC = text.search(/\.\ \(|\.\ \[|\ \(\[[sSdDcC]/); // /\(\[|\)\,|\)\)/);
  75. let testD = /(?<=\w. )(\(\[(Demo|Source Code|Clients)\]\([^)\]]*\)(, \[(Source Code|Clients)\]\([^)\]]*\))?(, \[(Source Code|Clients)\]\([^)\]]*\))*\))(?= \`?)/;
  76. const testD1 = /(^- \W[a-zA-Z0-9-_ .]*\W{0,2}http[^\[]*)(?<= )/;
  77. const testD2 = /(\`.*\` \`.*\`$)/;
  78. if ((testC > -1) && (!testD.test(text))) {
  79. let d1 = testD1.exec(text)[1];
  80. let d2 = testD2.exec(text)[1];
  81. return chalk.red(text.replace(d1, '').replace(d2, ''))
  82. } else if (testC > -1) {
  83. return chalk.green(testD.exec(text)[1])
  84. }
  85. return ""
  86. }
  87. // Tests '`License` `Language`'
  88. function testLangLic(text) {
  89. const testD2 = /(\`.*\` \`.*\`$)/;
  90. let testE = testD2.test(text);
  91. const testE1 = /(^[^`]*)/;
  92. if (!testE) {
  93. let e1 = testE1.exec(text)[1];
  94. return chalk.red(text.replace(e1, ''))
  95. }
  96. return chalk.green(testD2.exec(text)[1])
  97. }
  98. //Runs all the syntax tests...
  99. function findError(text) {
  100. let res
  101. res = testMainLink(text)
  102. res += testDescription(text)
  103. res += testSrcDemCli(text)
  104. res += testLangLic(text)
  105. return res + `\n`
  106. }
  107. //Check if license is in the list of licenses.
  108. function testLicense(md) {
  109. let pass = true;
  110. let lFailed = []
  111. let lPassed = []
  112. const regex = /.*\`(.*)\` .*$/;
  113. try {
  114. for (l of regex.exec(md)[1].split("/")) {
  115. if (!licenses.has(l)) {
  116. pass = false;
  117. lPassed.push(l)
  118. }
  119. lFailed.push(l)
  120. }
  121. }
  122. catch(err) {
  123. console.log(chalk.yellow("Error in License syntax, license not checked against list."))
  124. return [false, "", ""]
  125. }
  126. return [pass, lFailed, lPassed]
  127. }
  128. //Parses name from entry
  129. function parseName(md) {
  130. const regex = /^\W*(.*?)\W/
  131. return regex.exec(md)[1]
  132. }
  133. function entryErrorCheck() {
  134. const lines = split(readme); // Inserts each line into the entries array
  135. let totalFail = 0;
  136. let totalPass = 0;
  137. let total = 0;
  138. let entries = [];
  139. let diffEntries = [];
  140. if (lines[0] === "") {
  141. console.log(chalk.red("0 Entries Found, check your commandline arguments"))
  142. process.exit(0)
  143. }
  144. for (let i = 0; i < lines.length; i ++) { // Loop through array of lines
  145. if (entryFilter(lines[i]) === true) { // filter out lines that don't start with * [)
  146. e = {};
  147. e.raw = lines[i];
  148. e.line = i + 1
  149. entries.push(e);
  150. } else if (licenseFilter(lines[i]) === true) {
  151. licenses.add(parseLicense(lines[i]))
  152. }
  153. }
  154. if (pr === true) {
  155. console.log(chalk.cyan("Only testing the diff from the PR.\n"))
  156. const diffLines = split(diff); // Inserts each line of diff into an array
  157. for (let l of diffLines) {
  158. if (entryFilter(l) === true) { // filter out lines that don't start with * [)
  159. e = {};
  160. e.raw = l;
  161. diffEntries.push(e);
  162. } else if (licenseFilter(l) === true) {
  163. licenses.add(parseLicense(l))
  164. }
  165. }
  166. if (diffEntries.length === 0) {
  167. console.log("No entries changed in README.md, Exiting...")
  168. process.exit(0)
  169. }
  170. total = diffEntries.length
  171. for (let e of diffEntries) {
  172. e.pass = true
  173. e.name = parseName(e.raw)
  174. if (!findPattern(e.raw)) {
  175. e.highlight = findError(e.raw);
  176. e.pass = false;
  177. console.log(e.highlight)
  178. }
  179. e.licenseTest = testLicense(e.raw);
  180. if (!e.licenseTest) {
  181. e.pass = false;
  182. console.log(chalk.red(`${e.name}'s license is not on License list.`))
  183. }
  184. if (e.pass) {
  185. totalPass++
  186. } else {
  187. totalFail++
  188. }
  189. }
  190. } else {
  191. console.log(chalk.cyan("Testing entire README.md\n"))
  192. total = entries.length
  193. for (let e of entries) {
  194. e.pass = true
  195. e.name = parseName(e.raw)
  196. if (!findPattern(e.raw)) {
  197. e.highlight = findError(e.raw);
  198. e.pass = false;
  199. console.log(`${chalk.yellow(e.line + ": ")}${e.highlight}`);
  200. syntax = e.highlight;
  201. }
  202. e.licenseTest = testLicense(e.raw);
  203. if (!e.licenseTest[0]) {
  204. e.pass = false;
  205. console.log(chalk.yellow(e.line + ": ") + `${e.name}'s license ${chalk.red(`'${e.licenseTest[1]}'`)} is not on the License list.\n`)
  206. }
  207. if (e.pass) {
  208. totalPass++
  209. } else {
  210. totalFail++
  211. }
  212. }
  213. }
  214. if (totalFail > 0) {
  215. console.log(chalk.blue(`\n-----------------------------\n`))
  216. console.log(chalk.red(`${totalFail} Failed, `) + chalk.green(`${totalPass} Passed, `) + chalk.blue(`of ${total}`))
  217. console.log(chalk.blue(`\n-----------------------------\n`))
  218. process.exit(1);
  219. } else {
  220. console.log(chalk.blue(`\n-----------------------------\n`))
  221. console.log(chalk.green(`${totalPass} Passed of ${total}`))
  222. console.log(chalk.blue(`\n-----------------------------\n`))
  223. process.exit(0)
  224. }
  225. }
  226. parseArgs(process.argv)
  227. entryErrorCheck();