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.

241 lines
7.0 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. //Tests '- [Name](http://homepage/)'
  51. function testMainLink(text) {
  52. let testA = /(^ {0,2}- \[.*?\]\(.*\))(?=.?-? ?\w)/;
  53. const testA1 = /(- \[.*?\]?\(?.*?\)?)( .*$)/;
  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. //Tests '`⚠` - Short description, less than 250 characters.'
  61. function testDescription(text) {
  62. const testB = /( - .*\. )(?:(\(?\[?|\`))/;
  63. const testA1 = /(- \[.*?\]?\(?.*?\)?)( .*$)/;
  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(/\(\[|\)\,|\)\)/);
  75. let testD = /(?<=\w. )(\(\[(Demo|Source Code|Clients)\]\([^)]*\)(, \[(Source Code|Clients)\]\([^)]*\))?(, \[(Source Code|Clients)\]\([^)]*\))*\))(?= \`?)/;
  76. const testD1 = /(^.*\.)(?= )/;
  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. for (l of regex.exec(md)[1].split("/")) {
  114. if (!licenses.has(l)) {
  115. pass = false;
  116. lPassed.push(l)
  117. }
  118. lFailed.push(l)
  119. }
  120. return [pass, lFailed, lPassed]
  121. }
  122. //Parses name from entry
  123. function parseName(md) {
  124. const regex = /^\W*(.*?)\W/
  125. return regex.exec(md)[1]
  126. }
  127. function entryErrorCheck() {
  128. const lines = split(readme); // Inserts each line into the entries array
  129. let totalFail = 0;
  130. let totalPass = 0;
  131. let total = 0;
  132. let entries = [];
  133. let diffEntries = [];
  134. if (lines[0] === "") {
  135. console.log(chalk.red("0 Entries Found, check your commandline arguments"))
  136. process.exit(0)
  137. }
  138. for (let i = 0; i < lines.length; i ++) { // Loop through array of lines
  139. if (entryFilter(lines[i]) === true) { // filter out lines that don't start with * [)
  140. e = {};
  141. e.raw = lines[i];
  142. e.line = i
  143. entries.push(e);
  144. } else if (licenseFilter(lines[i]) === true) {
  145. licenses.add(parseLicense(lines[i]))
  146. }
  147. }
  148. if (pr === true) {
  149. console.log(chalk.cyan("Only testing the diff from the PR.\n"))
  150. const diffLines = split(diff); // Inserts each line of diff into an array
  151. for (let l of diffLines) {
  152. if (entryFilter(l) === true) { // filter out lines that don't start with * [)
  153. e = {};
  154. e.raw = l;
  155. diffEntries.push(e);
  156. } else if (licenseFilter(l) === true) {
  157. licenses.add(parseLicense(l))
  158. }
  159. }
  160. if (diffEntries.length === 0) {
  161. console.log("No entries changed in README.md, Exiting...")
  162. process.exit(0)
  163. }
  164. total = diffEntries.length
  165. for (let e of diffEntries) {
  166. e.pass = true
  167. e.name = parseName(e.raw)
  168. if (!findPattern(e.raw)) {
  169. e.highlight = findError(e.raw);
  170. e.pass = false;
  171. console.log(`${e.highlight}`)
  172. }
  173. e.licenseTest = testLicense(e.raw);
  174. if (!e.licenseTest) {
  175. e.pass = false;
  176. console.log(chalk.red(`${e.name}'s license is not on License list.`))
  177. }
  178. if (e.pass) {
  179. totalPass++
  180. } else {
  181. totalFail++
  182. }
  183. }
  184. } else {
  185. console.log(chalk.cyan("Testing entire README.md\n"))
  186. total = entries.length
  187. for (let e of entries) {
  188. e.pass = true
  189. e.name = parseName(e.raw)
  190. if (!findPattern(e.raw)) {
  191. e.highlight = findError(e.raw);
  192. e.pass = false;
  193. console.log(`${chalk.yellow(e.line + ": ")}${e.highlight}`);
  194. syntax = e.highlight;
  195. }
  196. e.licenseTest = testLicense(e.raw);
  197. if (!e.licenseTest[0]) {
  198. e.pass = false;
  199. console.log(chalk.yellow(e.line + ": ") + `${e.name}'s license ${chalk.red(`'${e.licenseTest[1]}'`)} is not on the License list.\n`)
  200. }
  201. if (e.pass) {
  202. totalPass++
  203. } else {
  204. totalFail++
  205. }
  206. }
  207. }
  208. if (totalFail > 0) {
  209. console.log(chalk.blue(`\n-----------------------------\n`))
  210. console.log(chalk.red(`${totalFail} Failed, `) + chalk.green(`${totalPass} Passed, `) + chalk.blue(`of ${total}`))
  211. console.log(chalk.blue(`\n-----------------------------\n`))
  212. process.exit(1);
  213. } else {
  214. console.log(chalk.blue(`\n-----------------------------\n`))
  215. console.log(chalk.green(`${totalPass} Passed of ${total}`))
  216. console.log(chalk.blue(`\n-----------------------------\n`))
  217. process.exit(0)
  218. }
  219. }
  220. parseArgs(process.argv)
  221. entryErrorCheck();