From 5c6a0356c0372922ee697174432021c0bd250a7a Mon Sep 17 00:00:00 2001 From: Mayursinh Sarvaiya Date: Fri, 2 Oct 2020 10:50:47 +0530 Subject: [PATCH] feat: TechDocs - Add vale linter to check words quality in md files. (#2631) * fix(docs): typos which were reflacted from vale linter's command * feat: Implement Vale linter (#2031) Initialize .vale.ini file Add 'lint:docs' script to package.json, to lint all md files except the ones which are located in node_modules Generate 'vocab.txt' by using command 'yarn run lint:docs' | grep -o ''[a-z A-Z]*'' | grep -o '[a-z A-Z]*' | sort | uniq > .github/styles/vocab.txt Add steps to github workflow 'master' to check docs quality * chore: Separate workflow for quality checking * chore: Added 'shx' dev dependency to support grep command in cross platform * feat: Add script to operate same quality check process on different platform * ignore: remove lint:docs from lint-stages which was added for experiment purpose * fix: check-all-files on push event & check-changed-files on pull_request event * chore(CI): triggle workflow only when there is any updates in .md file(s) on pull request * fix: use spawnSync to solve 'The command line is too long.' error * fix: github workflow syntax * fix: prettier error * chore: add vale command directly to lint-staged * chore: use shebang for easy access * fix: windows script issue & remove shebang * chore: Add shebang flag * chore: better error message related to vale * chore: mention vale linter in documentation * fix: spelling errors & add keywords to vocab.txt --- .github/styles/vocab.txt | 203 ++++++++++++++++++ .github/workflows/docs-quality-checker.yml | 18 ++ .vale.ini | 4 + CONTRIBUTING.md | 2 + DEPLOYMENT.md | 2 +- .../extending/create-your-own-preparer.md | 2 +- docs/features/techdocs/FAQ.md | 2 +- docs/plugins/testing.md | 2 +- docs/reference/utility-apis/AlertApi.md | 2 +- docs/reference/utility-apis/AppThemeApi.md | 2 +- docs/reference/utility-apis/ErrorApi.md | 2 +- .../reference/utility-apis/OAuthRequestApi.md | 7 +- docs/reference/utility-apis/SessionApi.md | 2 +- .../reference/utility-apis/SessionStateApi.md | 2 +- docs/reference/utility-apis/StorageApi.md | 6 +- package.json | 7 +- .../StructuredMetadataTable/README.md | 2 +- .../mock-docs/docs/index.md | 2 +- plugins/api-docs/README.md | 2 +- plugins/cost-insights/README.md | 2 +- scripts/check-docs-quality.js | 68 ++++++ yarn.lock | 16 +- 22 files changed, 336 insertions(+), 21 deletions(-) create mode 100644 .github/styles/vocab.txt create mode 100644 .github/workflows/docs-quality-checker.yml create mode 100644 .vale.ini create mode 100755 scripts/check-docs-quality.js diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt new file mode 100644 index 0000000000000..21045f04dd617 --- /dev/null +++ b/.github/styles/vocab.txt @@ -0,0 +1,203 @@ +abc +Apdex +api +Api +apis +args +asciidoc +async +Avro +backrub +Balachandran +Bigtable +Blackbox +bool +boolean +Chai +changeset +changesets +Changesets +changset +chanwit +Chanwit +cisphobia +cissexist +classname +cli +cncf +codeblocks +Codecov +codehilite +Codehilite +codeowners +config +Config +configs +const +cookiecutter +css +dariddler +deadnaming +destructured +dev +devs +discoverability +Discoverability +dls +docgen +Dockerfile +Dockerize +dockerode +Docusaurus +eg +Ek +env +Env +eslint +facto +failover +Figma +Firekube +Fredrik +github +Github +Gitlab +graphql +graphviz +Hackathons +haproxy +heroku +Hostname +http +https +img +incentivised +inlinehilite +interop +javascript +Javascript +jq +js +json +jsx +Kaewkasi +Knex +kubectl +kubernetes +learnings +lerna +Lerna +magiclink +mailto +Malus +md +microsite +middleware +minikube +Minikube +misgendering +mkdocs +Mkdocs +monorepo +Monorepo +monorepos +msw +namespace +Namespaces +neuro +newrelic +nginx +Niklas +nohoist +nonces +npm +nvm +oauth +Oauth +Okta +Oldsberg +onboarding +Onboarding +pagerduty +Patrik +Phoen +plantuml +Pomaceous +postgres +pre +prebaked +preconfigured +Preprarer +Prerequisities +productional +Protobuf +proxying +Proxying +pygments +pymdownx +Raghunandan +rankdir +readme +Readme +Redash +repo +Repo +repos +rerender +rollbar +Rollbar +Rollup +Rosaceae +rst +rsync +ruleset +sam +scaffolded +scaffolder +Scaffolder +semlas +Serverless +Sinon +smartsymobls +sparklines +Spotifiers +spotify +Spotify +squidfunk +src +subkey +superfences +Superfences +talkdesk +Talkdesk +tasklist +techdocs +templated +templater +Templater +templaters +Templaters +Thauer +theres +toc +tolerations +Tolerations +toolsets +touchpoints +ui +upvote +url +utils +validators +Voi +Wealthsimple +Weaveworks +xyz +yaml +Zalando +Zhou +Billett +cloudbuild +Grafana +Iain +Snyk diff --git a/.github/workflows/docs-quality-checker.yml b/.github/workflows/docs-quality-checker.yml new file mode 100644 index 0000000000000..45cf1dafe3e84 --- /dev/null +++ b/.github/workflows/docs-quality-checker.yml @@ -0,0 +1,18 @@ +name: Check Markdown files quality + +on: + pull_request: + branches: [master] + paths: + - '**.md' + +jobs: + check-all-files: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: documentation quality check + uses: errata-ai/vale-action@v1.3.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.vale.ini b/.vale.ini new file mode 100644 index 0000000000000..fd1ea53b0dfc2 --- /dev/null +++ b/.vale.ini @@ -0,0 +1,4 @@ +StylesPath = .github/styles + +[*.md] +BasedOnStyles = Vale diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2b6b49e1f8b50..85834076d4d39 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,6 +76,8 @@ If you're contributing to the backend or CLI tooling, be mindful of cross-platfo Also be sure to skim through our [ADRs](https://github.com/spotify/backstage/tree/master/docs/architecture-decisions) to see if they cover what you're working on. In particular [ADR006: Avoid React.FC and React.SFC](https://github.com/spotify/backstage/blob/master/docs/architecture-decisions/adr006-avoid-react-fc.md) is one to look out for. +If there are any updates in `markdown` file please make sure to run `yarn run lint:docs`. Though it is checked on `lint-staged`. It is required to install [vale](https://docs.errata.ai/vale/install) separately and make sure it is accessed by global command. + # Creating Changesets We use [changesets](https://github.com/atlassian/changesets) to help us prepare releases. It helps us make sure that every package affected by a change gets a proper version number and an entry in its `CHANGELOG.md`. To make the process of generating releases easy. it helps when contributors include changesets with their pull requests. diff --git a/DEPLOYMENT.md b/DEPLOYMENT.md index be77f0d4de07f..1aebbeb0ebe65 100644 --- a/DEPLOYMENT.md +++ b/DEPLOYMENT.md @@ -4,7 +4,7 @@ Deploying to heroku is relatively easy following these steps. -First, make sure you have the [heroku CLI installed](https://devcenter.heroku.com/articles/heroku-cli) and log into it as well as loging into Heroku's [container registry](https://devcenter.heroku.com/articles/container-registry-and-runtime). +First, make sure you have the [heroku CLI installed](https://devcenter.heroku.com/articles/heroku-cli) and log into it as well as login into Heroku's [container registry](https://devcenter.heroku.com/articles/container-registry-and-runtime). ```bash $ heroku login diff --git a/docs/features/software-templates/extending/create-your-own-preparer.md b/docs/features/software-templates/extending/create-your-own-preparer.md index e354c0941e0e8..80287c6ce1724 100644 --- a/docs/features/software-templates/extending/create-your-own-preparer.md +++ b/docs/features/software-templates/extending/create-your-own-preparer.md @@ -88,7 +88,7 @@ Some good examples exist here: - https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts - https://github.com/spotify/backstage/blob/master/plugins/scaffolder-backend/src/scaffolder/stages/prepare/github.ts -### Registerinng your own Preparer +### Registering your own Preparer You can register the preparer that you have created with the `PreparerBuilder` by using the `PreparerKey` from the Catalog, for example like this: diff --git a/docs/features/techdocs/FAQ.md b/docs/features/techdocs/FAQ.md index 1c5c4795bebcb..e81e9d82c7f13 100644 --- a/docs/features/techdocs/FAQ.md +++ b/docs/features/techdocs/FAQ.md @@ -35,4 +35,4 @@ well as a selection of Python Markdown extensions that TechDocs supports. Not right now. We are currently using MkDocs to generate the documentation from source, so the files have to be in Markdown format. However, in the future we want to support other static site generators which will make it possible to use -otherfile formats. +other file formats. diff --git a/docs/plugins/testing.md b/docs/plugins/testing.md index bb5a577c7d108..c6e1599786f36 100644 --- a/docs/plugins/testing.md +++ b/docs/plugins/testing.md @@ -166,7 +166,7 @@ because it fulfills all the principles above: ✓ **Fulfills Input/Output Principle**: Verifies the output changes when the input changes -✓ **Fufills Blackbox Principle**: Does not verify _how_ the `` +✓ **Fulfills Blackbox Principle**: Does not verify _how_ the `` component is mounted, just that it is mounted in response to the input. ✓ **Fulfills Scalability Principle**: If we decide to refactor the entire way diff --git a/docs/reference/utility-apis/AlertApi.md b/docs/reference/utility-apis/AlertApi.md index d096276c3bfd9..6219f520f59fc 100644 --- a/docs/reference/utility-apis/AlertApi.md +++ b/docs/reference/utility-apis/AlertApi.md @@ -73,7 +73,7 @@ Referenced by: [alert\$](#alert). ### Observer -This file contains non-react related core types used throught Backstage. +This file contains non-react related core types used through Backstage. Observer interface for consuming an Observer, see TC39. diff --git a/docs/reference/utility-apis/AppThemeApi.md b/docs/reference/utility-apis/AppThemeApi.md index d37282adea328..eb35eb38efb63 100644 --- a/docs/reference/utility-apis/AppThemeApi.md +++ b/docs/reference/utility-apis/AppThemeApi.md @@ -135,7 +135,7 @@ Referenced by: [activeThemeId\$](#activethemeid). ### Observer -This file contains non-react related core types used throught Backstage. +This file contains non-react related core types used through Backstage. Observer interface for consuming an Observer, see TC39. diff --git a/docs/reference/utility-apis/ErrorApi.md b/docs/reference/utility-apis/ErrorApi.md index 4de58b34a153e..762d2016d501f 100644 --- a/docs/reference/utility-apis/ErrorApi.md +++ b/docs/reference/utility-apis/ErrorApi.md @@ -93,7 +93,7 @@ Referenced by: [error\$](#error). ### Observer -This file contains non-react related core types used throught Backstage. +This file contains non-react related core types used through Backstage. Observer interface for consuming an Observer, see TC39. diff --git a/docs/reference/utility-apis/OAuthRequestApi.md b/docs/reference/utility-apis/OAuthRequestApi.md index ac11764682ad4..1328aabeecf82 100644 --- a/docs/reference/utility-apis/OAuthRequestApi.md +++ b/docs/reference/utility-apis/OAuthRequestApi.md @@ -11,7 +11,8 @@ The following Utility API implements this type: ### createAuthRequester() A utility for showing login popups or similar things, and merging together -multiple requests for different scopes into one request that inclues all scopes. +multiple requests for different scopes into one request that includes all +scopes. The passed in options provide information about the login provider, and how to handle auth requests. @@ -30,7 +31,7 @@ createAuthRequester<AuthResponse>( ### authRequest\$() -Observers panding auth requests. The returned observable will emit all current +Observers pending auth requests. The returned observable will emit all current active auth request, at most one for each created auth requester. Each request has its own info about the login provider, forwarded from the auth @@ -156,7 +157,7 @@ Referenced by: [authRequest\$](#authrequest). ### Observer -This file contains non-react related core types used throught Backstage. +This file contains non-react related core types used through Backstage. Observer interface for consuming an Observer, see TC39. diff --git a/docs/reference/utility-apis/SessionApi.md b/docs/reference/utility-apis/SessionApi.md index 5e40b3317b036..e7a9e58c59489 100644 --- a/docs/reference/utility-apis/SessionApi.md +++ b/docs/reference/utility-apis/SessionApi.md @@ -81,7 +81,7 @@ Referenced by: [sessionState\$](#sessionstate). ### Observer -This file contains non-react related core types used throught Backstage. +This file contains non-react related core types used through Backstage. Observer interface for consuming an Observer, see TC39. diff --git a/docs/reference/utility-apis/SessionStateApi.md b/docs/reference/utility-apis/SessionStateApi.md index c1821f23818f2..81f25aa349dce 100644 --- a/docs/reference/utility-apis/SessionStateApi.md +++ b/docs/reference/utility-apis/SessionStateApi.md @@ -62,7 +62,7 @@ Referenced by: [sessionState\$](#sessionstate). ### Observer -This file contains non-react related core types used throught Backstage. +This file contains non-react related core types used through Backstage. Observer interface for consuming an Observer, see TC39. diff --git a/docs/reference/utility-apis/StorageApi.md b/docs/reference/utility-apis/StorageApi.md index 3fd75a42d6396..e7d5131ff9117 100644 --- a/docs/reference/utility-apis/StorageApi.md +++ b/docs/reference/utility-apis/StorageApi.md @@ -35,7 +35,7 @@ remove(key: string): Promise<void> ### set() -Save persistant data, and emit messages to anyone that is using observe\$ for +Save persistent data, and emit messages to anyone that is using observe\$ for this key
@@ -85,7 +85,7 @@ Referenced by: [observe\$](#observe), [StorageApi](#storageapi).
 
 ### Observer
 
-This file contains non-react related core types used throught Backstage.
+This file contains non-react related core types used through Backstage.
 
 Observer interface for consuming an Observer, see TC39.
 
@@ -129,7 +129,7 @@ export interface StorageApi {
   remove(key: string): Promise<void>;
 
   /**
-   * Save persistant data, and emit messages to anyone that is using observe$ for this key
+   * Save persistent data, and emit messages to anyone that is using observe$ for this key
    *
    * @param {String} key Unique key associated with the data.
    */
diff --git a/package.json b/package.json
index 45e13d30f4b06..b9c84a4d902cf 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "test": "lerna run test --since origin/master -- --coverage",
     "test:all": "lerna run test -- --coverage",
     "lint": "lerna run lint --since origin/master --",
+    "lint:docs": "node ./scripts/check-docs-quality",
     "lint:all": "lerna run lint --",
     "lint:type-deps": "node scripts/check-type-dependencies.js",
     "docgen": "lerna run docgen",
@@ -46,7 +47,8 @@
     "lerna": "^3.20.2",
     "lint-staged": "^10.1.0",
     "prettier": "^2.0.5",
-    "recursive-readdir": "^2.2.2"
+    "recursive-readdir": "^2.2.2",
+    "shx": "^0.3.2"
   },
   "husky": {
     "hooks": {
@@ -61,6 +63,9 @@
     ],
     "*.{json,md}": [
       "prettier --write"
+    ],
+    "*.md": [
+      "vale"
     ]
   },
   "jest": {
diff --git a/packages/core/src/components/StructuredMetadataTable/README.md b/packages/core/src/components/StructuredMetadataTable/README.md
index 452d32490fdf0..60952e5e34b93 100644
--- a/packages/core/src/components/StructuredMetadataTable/README.md
+++ b/packages/core/src/components/StructuredMetadataTable/README.md
@@ -23,7 +23,7 @@ The component itself only handles the display area, so you can use standard JS t
 
 This will step through each of the keys and based on their types display them in a logical way.
 
-### Primatives
+### Primitives
 
 Any non complex value will be displayed using `{value}` which will just output the value as text.
 
diff --git a/packages/techdocs-container/mock-docs/docs/index.md b/packages/techdocs-container/mock-docs/docs/index.md
index 3c323d6fc1eff..94a53acfa302c 100644
--- a/packages/techdocs-container/mock-docs/docs/index.md
+++ b/packages/techdocs-container/mock-docs/docs/index.md
@@ -1,7 +1,7 @@
 ## hello mock docs
 
 !!! test
-Testing somethin
+Testing something
 
 Abbreviations:
 Some text about MOCDOC
diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md
index 17f354e9978c4..ecc0e5a560242 100644
--- a/plugins/api-docs/README.md
+++ b/plugins/api-docs/README.md
@@ -18,7 +18,7 @@ Right now, the following API formats are supported:
 - [AsyncAPI](https://www.asyncapi.com/docs/specifications/latest/)
 - [GraphQL](https://graphql.org/learn/schema/)
 
-Other formats are displayed as plain text, but this can easily be extented.
+Other formats are displayed as plain text, but this can easily be extended.
 
 To fill the catalog with APIs, [provide entities of kind API](https://backstage.io/docs/features/software-catalog/descriptor-format#kind-api).
 To link that an component implements an API, see [`implementsApis` property on components](https://backstage.io/docs/features/software-catalog/descriptor-format#specimplementsapis-optional).
diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md
index ce0b68383bcfa..221f9f37d845f 100644
--- a/plugins/cost-insights/README.md
+++ b/plugins/cost-insights/README.md
@@ -81,7 +81,7 @@ costInsights:
 
 In the `Cost Overview` panel, users can choose from a dropdown of business metrics to see costs as they relate to a metric, such as daily active users. Metrics must be defined as keys on the `metrics` field. A user-friendly name is **required**. Metrics will be provided to the `getDailyCost` and `getProjectCosts` API methods via the `metric` parameter.
 
-**Note:** Cost Insights displays daily cost without a metric by default. The dropdown text for this default can be overriden by assigning it a value on the `dailyCost` field.
+**Note:** Cost Insights displays daily cost without a metric by default. The dropdown text for this default can be overridden by assigning it a value on the `dailyCost` field.
 
 ```yaml
 ## ./app-config.yaml
diff --git a/scripts/check-docs-quality.js b/scripts/check-docs-quality.js
new file mode 100755
index 0000000000000..61968a2ac0964
--- /dev/null
+++ b/scripts/check-docs-quality.js
@@ -0,0 +1,68 @@
+#!/usr/bin/env node
+/*
+ * Copyright 2020 Spotify AB
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const { execSync, spawnSync } = require('child_process');
+const path = require('path');
+
+const listFilesTrackedByGit = 'git ls-files';
+
+const inheritStdIo = {
+  stdio: 'inherit',
+};
+
+const ERROR_MESSAGE =
+  'Please install vale linter(https://docs.errata.ai/vale/install). Ignore this message if already installed.\n';
+
+// xargs is not supported by shx.
+if (process.platform === 'win32') {
+  const validMDFilesCommand = `${listFilesTrackedByGit} | .\\node_modules\\.bin\\shx grep ".md"`;
+  try {
+    // get list of all md files except in directories of gitignore.
+    let filesToLint = execSync(validMDFilesCommand, {
+      stdio: ['ignore', 'pipe', 'inherit'],
+    });
+
+    // set all file(s) path as absolute path
+    filesToLint = filesToLint
+      .toString()
+      .split('\n')
+      .map(filepath => (filepath ? path.join(process.cwd(), filepath) : null))
+      .filter(Boolean);
+
+    const output = spawnSync('vale', filesToLint, inheritStdIo);
+
+    // if the command does not succeed
+    if (output.status !== 0) {
+      // if it contains system level error. [in this case vale does not exist]
+      if (output.error) {
+        console.error(ERROR_MESSAGE);
+      }
+      process.exit(1);
+    }
+  } catch (e) {
+    console.error(e.message);
+    process.exit(1);
+  }
+} else {
+  const validMDFilesCommand = `${listFilesTrackedByGit} | ./node_modules/.bin/shx grep ".md"`;
+  // use xargs
+  try {
+    execSync(`${validMDFilesCommand} | xargs vale`, inheritStdIo);
+  } catch (e) {
+    console.error(ERROR_MESSAGE);
+    process.exit(1);
+  }
+}
diff --git a/yarn.lock b/yarn.lock
index 9cee3c360218c..c5429eafcec67 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -10430,6 +10430,11 @@ es6-iterator@^2.0.3, es6-iterator@~2.0.3:
     es5-ext "^0.10.35"
     es6-symbol "^3.1.1"
 
+es6-object-assign@^1.0.3:
+  version "1.1.0"
+  resolved "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
+  integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=
+
 es6-promise@^4.0.3:
   version "4.2.8"
   resolved "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a"
@@ -20703,7 +20708,7 @@ shell-quote@1.7.2:
   resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
   integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
 
-shelljs@^0.8.2, shelljs@^0.8.3:
+shelljs@^0.8.1, shelljs@^0.8.2, shelljs@^0.8.3:
   version "0.8.4"
   resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2"
   integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==
@@ -20724,6 +20729,15 @@ shortid@^2.2.14:
   dependencies:
     nanoid "^2.1.0"
 
+shx@^0.3.2:
+  version "0.3.2"
+  resolved "https://registry.npmjs.org/shx/-/shx-0.3.2.tgz#40501ce14eb5e0cbcac7ddbd4b325563aad8c123"
+  integrity sha512-aS0mWtW3T2sHAenrSrip2XGv39O9dXIFUqxAEWHEOS1ePtGIBavdPJY1kE2IHl14V/4iCbUiNDPGdyYTtmhSoA==
+  dependencies:
+    es6-object-assign "^1.0.3"
+    minimist "^1.2.0"
+    shelljs "^0.8.1"
+
 side-channel@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947"