diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt
new file mode 100644
index 0000000000..21045f04dd
--- /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 0000000000..45cf1dafe3
--- /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 0000000000..fd1ea53b0d
--- /dev/null
+++ b/.vale.ini
@@ -0,0 +1,4 @@
+StylesPath = .github/styles
+
+[*.md]
+BasedOnStyles = Vale
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2b6b49e1f8..85834076d4 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 be77f0d4de..1aebbeb0eb 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 e354c0941e..80287c6ce1 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 1c5c4795be..e81e9d82c7 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 bb5a577c7d..c6e1599786 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 `
@@ -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 45e13d30f4..b9c84a4d90 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 452d32490f..60952e5e34 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 3c323d6fc1..94a53acfa3 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 17f354e997..ecc0e5a560 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 ce0b68383b..221f9f37d8 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 0000000000..61968a2ac0
--- /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 9cee3c3602..c5429eafce 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"