diff --git a/.github/styles/vocab.txt b/.github/vale/Vocab/Backstage/accept.txt similarity index 98% rename from .github/styles/vocab.txt rename to .github/vale/Vocab/Backstage/accept.txt index acbc47a861..b21f38c2ab 100644 --- a/.github/styles/vocab.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -8,9 +8,6 @@ airbrake Anddddd Apdex api -Api -apis -args asciidoc async Atlassian @@ -19,7 +16,6 @@ autoscaling Autoscaling autoselect Avro -aws backported backporting Bigtable @@ -38,7 +34,6 @@ changesets Changesets chanwit Chanwit -ci CI/CD classname cli @@ -127,7 +122,6 @@ hoc horizontalpodautoscalers Hostname hotspots -html http https Iain @@ -314,7 +308,6 @@ templaters Templaters theia thumbsup -toc todo tolerations Tolerations @@ -339,7 +332,6 @@ unregistration untracked upsert upvote -url URIs URLs utils diff --git a/.github/vale/config.ini b/.github/vale/config.ini new file mode 100644 index 0000000000..36acb2c058 --- /dev/null +++ b/.github/vale/config.ini @@ -0,0 +1,6 @@ +StylesPath = . +Vocab = Backstage + +[*.md] +BasedOnStyles = Vale +Vale.Terms = NO diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 55c1c23587..ad2e92799f 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -12,10 +12,18 @@ jobs: steps: - uses: actions/checkout@v2 + + # Vale does not support file excludes, so we use the script to generate a list of files instead + # The action also does not allow args or a local config file to be passed in, so the files array + # also contains an "--config=.github/vale/config.ini" option + - name: generate vale args + id: generate + run: echo "::set-output name=args::$(node scripts/check-docs-quality.js --ci-args)" + - name: documentation quality check - uses: errata-ai/vale-action@v1.4.0 - # Whitelist excluding ADOPTERS, CHANGELOG and OWNERS (no exclude flag exists) + uses: errata-ai/vale-action@v1.5.0 with: - files: '[".changeset", ".github", "contrib", "docs", "microsite", "packages", "plugins", "CONTRIBUTING.md", "CODE_OF_CONDUCT.md", "GOVERNANCE.md", "README.md"]' + # This also contains --config=.github/vale/config.ini ... :/ + files: '${{ steps.generate.outputs.args }}' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e4aad09207..ca7af55d3b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,7 +78,7 @@ If you're contributing to the backend or CLI tooling, be mindful of cross-platfo Also be sure to skim through our [ADRs](docs/architecture-decisions) to see if they cover what you're working on. In particular [ADR006: Avoid React.FC and React.SFC](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) `1.4.0` separately and make sure it is accessed by global command. +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. ## Developer Certificate of Origin diff --git a/STYLE.md b/STYLE.md index 391b31ce50..b28c5da4fa 100644 --- a/STYLE.md +++ b/STYLE.md @@ -146,7 +146,7 @@ There are a few things to pay attention to, in order to make the documentation s API documenter will not recognize arrow functions as functions, but rather as a constant that shows up in the list of exported variables. By declaring functions using the `function` keyword, they will show up in the list of functions. They will also get a much nicer documentation page for the individual function that shows information about parameters and return types. -This also extends to React components, since API documenter doesn't have any special handling of those. By always defining exported React components using the `function` keyword, we make them show up among the list of functions in the API reference, where they are then easily discoverable through the `(props)` args (which you should be sure to include!). +This also extends to React components, since API documenter doesn't have any special handling of those. By always defining exported React components using the `function` keyword, we make them show up among the list of functions in the API reference, where they are then easily discoverable through the `(props)` argument (which you should be sure to include!). ![image](https://user-images.githubusercontent.com/4984472/133120461-59d74c3e-ebd9-44f9-900d-cc30f54a3cd2.png) diff --git a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md index 8a4d6fb273..9ec3f1ca64 100644 --- a/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md +++ b/contrib/docs/tutorials/help-im-behind-a-corporate-proxy.md @@ -64,8 +64,8 @@ if (process.env.HTTPS_PROXY) { If your development environment is in the cloud (like with [AWS Cloud9](https://aws.amazon.com/cloud9/) or an instance of [Theia](https://theia-ide.org/)), you will need to update your configuration. -You will probably need to make some changes in `app-config.yaml` (or another config file like `app-config.local.yaml` if you've created it, see the [configuration doc](https://backstage.io/docs/conf/#supplying-configuration)). -The exact values will depend on your setup but for instance, if your public url is `https://your-public-url.com` and the port `3000` and `8080` are open: +You will probably need to make some changes in `app-config.yaml` (or another config file like `app-config.local.yaml` if you've created it, see the [configuration doc](https://backstage.io/docs/conf/#supplying-configuration)). +The exact values will depend on your setup but for instance, if your public URL is `https://your-public-url.com` and the port `3000` and `8080` are open: ```yaml app: diff --git a/docs/auth/add-auth-provider.md b/docs/auth/add-auth-provider.md index 068ba01460..282088fef9 100644 --- a/docs/auth/add-auth-provider.md +++ b/docs/auth/add-auth-provider.md @@ -230,7 +230,7 @@ name. ### Test the new provider You can `curl -i localhost:7007/api/auth/providerA/start` and which should -provide a `302` redirect with a `Location` header. Paste the url from that +provide a `302` redirect with a `Location` header. Paste the URL from that header into a web browser and you should be able to trigger the authorization flow. diff --git a/docs/auth/github/provider.md b/docs/auth/github/provider.md index 14b99bfea5..a8c6020cae 100644 --- a/docs/auth/github/provider.md +++ b/docs/auth/github/provider.md @@ -50,7 +50,7 @@ The GitHub provider is a structure with three configuration keys: - `clientSecret`: The client secret tied to the generated client ID. - `enterpriseInstanceUrl` (optional): The base URL for a GitHub Enterprise instance, e.g. `https://ghe..com`. Only needed for GitHub Enterprise. -- `callbackUrl` (optional): The callback url that GitHub will use when +- `callbackUrl` (optional): The callback URL that GitHub will use when initiating an OAuth flow, e.g. `https://your-intermediate-service.com/handler`. Only needed if Backstage is not the immediate receiver (e.g. one OAuth app for many backstage instances). diff --git a/docs/conf/writing.md b/docs/conf/writing.md index a4da1e3df0..aef9620373 100644 --- a/docs/conf/writing.md +++ b/docs/conf/writing.md @@ -161,7 +161,7 @@ $file: ./my-secret.txt The `$include` keyword can be used to load configuration values from an external file. It's able to load and parse data from `.json`, `.yml`, and `.yaml` files. -It's also possible to include a url fragment (`#`) to point to a value at the +It's also possible to include a URL fragment (`#`) to point to a value at the given path in the file, using a dot-separated list of keys. For example, the following would read `my-secret-key` from `my-secrets.json`: diff --git a/docs/features/techdocs/concepts.md b/docs/features/techdocs/concepts.md index c96db5e3b7..2944197dfb 100644 --- a/docs/features/techdocs/concepts.md +++ b/docs/features/techdocs/concepts.md @@ -18,8 +18,8 @@ GitLab, etc.) and passes the files to the generator for next steps. There are two kinds of preparers available - -1. Common Git Preparer - Uses `git clone` on any repository url. -2. Url Reader - Uses source code hosting provider's API to download files. +1. Common Git Preparer - Uses `git clone` on any repository URL. +2. URL Reader - Uses source code hosting provider's API to download files. (Faster and recommended) ### TechDocs Generator diff --git a/docs/features/techdocs/using-cloud-storage.md b/docs/features/techdocs/using-cloud-storage.md index 66eaf00663..5a53961e81 100644 --- a/docs/features/techdocs/using-cloud-storage.md +++ b/docs/features/techdocs/using-cloud-storage.md @@ -461,7 +461,7 @@ Since the new SDK doesn't use the old way authentication, we don't need the keys The new SDK needs the OpenStack Swift connection URL for connecting the Swift. So you need to add a new key called `openStackSwift.swiftUrl` and give the -OpenStack Swift url here. Example url should look like that: +OpenStack Swift URL here. Example URL should look like that: `https://example.com:6780/swift/v1` ##### That's it! diff --git a/docs/getting-started/project-structure.md b/docs/getting-started/project-structure.md index b67e847272..cbe84db2e0 100644 --- a/docs/getting-started/project-structure.md +++ b/docs/getting-started/project-structure.md @@ -24,7 +24,7 @@ the code. - [`.github/`](https://github.com/backstage/backstage/tree/master/.github) - Standard GitHub folder. It contains - amongst other things - our workflow definitions and templates. Worth noting is the - [styles](https://github.com/backstage/backstage/tree/master/.github/styles) + [vale](https://github.com/backstage/backstage/tree/master/.github/vale) sub-folder which is used for a markdown spellchecker. - [`.yarn/`](https://github.com/backstage/backstage/tree/master/.yarn) - @@ -212,9 +212,6 @@ future. common for companies to have their own npm registry, and this file makes sure that this folder always uses the public registry. -- [`.vale.ini`](https://github.com/backstage/backstage/tree/master/.vale.ini) - - [Spell checker](https://github.com/errata-ai/vale) for Markdown files. - - [`.yarnrc`](https://github.com/backstage/backstage/tree/master/.yarnrc) - Enforces "our" version of Yarn. diff --git a/docs/integrations/gerrit/locations.md b/docs/integrations/gerrit/locations.md index dcbac65849..9bff2385e3 100644 --- a/docs/integrations/gerrit/locations.md +++ b/docs/integrations/gerrit/locations.md @@ -34,9 +34,9 @@ a structure with up to six elements: - `baseUrl` (optional): Needed if the Gerrit instance is not reachable at the base of the `host` option (e.g. `https://gerrit.company.com`) set the address here. This is the address that you would open in a browser. -- `cloneUrl` (optional): The base url for HTTP clones. Will default to `baseUrl` if +- `cloneUrl` (optional): The base URL for HTTP clones. Will default to `baseUrl` if not set. The address used to clone a repo is the `cloneUrl` plus the repo name. -- `gitilesBaseUrl` (optional): This is needed for creating a valid user-friendly url +- `gitilesBaseUrl` (optional): This is needed for creating a valid user-friendly URL that can be used for browsing the content of the provider. If not set a default value will be created in the same way as the "baseUrl" option. There is no requirement to have Gitiles for the Backstage Gerrit integration but without it diff --git a/docs/integrations/google-cloud-storage/locations.md b/docs/integrations/google-cloud-storage/locations.md index 16e19ea350..e15cb6346c 100644 --- a/docs/integrations/google-cloud-storage/locations.md +++ b/docs/integrations/google-cloud-storage/locations.md @@ -48,7 +48,7 @@ you can check [this documentation page][google gcs docs]. To use this integration to import entities from a GCS bucket go to the Google console and browse the file you would like to import. Then copy the `Authenticated URL` and paste it into the text box in the `register component` -form. This url should look like +form. This URL should look like `https://storage.cloud.google.com///catalog-info.yaml`. [google gcs docs]: https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-nodejs diff --git a/docs/tutorials/using-backstage-proxy-within-plugin.md b/docs/tutorials/using-backstage-proxy-within-plugin.md index 771aa7a652..3002eb1aff 100644 --- a/docs/tutorials/using-backstage-proxy-within-plugin.md +++ b/docs/tutorials/using-backstage-proxy-within-plugin.md @@ -52,7 +52,7 @@ calling `${backend-url}/api/proxy/`. The reason why `backend-url` is referenced is because the backstage backend creates and runs the proxy. Backstage is structured in such a way that you could run the backstage frontend independently of the backend. So when calling your API you -need to prepend the backend url to your http call. +need to prepend the backend URL to your http call. The recommended pattern for calling out to services is to wrap your calls in a [Utility API](../api/utility-apis.md). This section describes the steps to wrap diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index 51588aacf4..bea820cb61 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -156,7 +156,7 @@ The Swagger UI package by expects to have a route to `/oauth2-redirect.html` whi the redirect callback for the OAuth2 Authorization Code flow, however, this file is not installed by this plugin. -Grab a copy of [oauth2-redirect.html](https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html) +Grab a copy of [`oauth2-redirect.html`](https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html) and put it in the `app/public/` directory in order to enable Swagger UI to complete this redirection. This also may require you to adjust `Content Security Policy` header settings of your Backstage application, so that the script in `oauth2-redirect.html` can be executed. Since the script is static we can add the hash of it directly to our CSP policy, which we do by adding the following to the `csp` section of the app configuration: diff --git a/plugins/cost-insights/src/alerts/README.md b/plugins/cost-insights/src/alerts/README.md index 4b24e4005f..31efe57797 100644 --- a/plugins/cost-insights/src/alerts/README.md +++ b/plugins/cost-insights/src/alerts/README.md @@ -35,7 +35,7 @@ export class CostInsightsClient extends CostInsightsApi { ### Custom Setup -Default properties such as the title, subtitle and instructions page url can be overridden - even default UI such as chart itself. Additionally, alerts can be extended to support actions such as snoozing or dismissing. +Default properties such as the title, subtitle and instructions page URL can be overridden - even default UI such as chart itself. Additionally, alerts can be extended to support actions such as snoozing or dismissing. ![project-growth-alert-custom](../assets/project-growth-alert-custom.png) diff --git a/plugins/kubernetes-backend/examples/dice-roller/README.md b/plugins/kubernetes-backend/examples/dice-roller/README.md index d4725e38c0..f7bcdd3973 100644 --- a/plugins/kubernetes-backend/examples/dice-roller/README.md +++ b/plugins/kubernetes-backend/examples/dice-roller/README.md @@ -16,7 +16,7 @@ This can be used to run the kubernetes plugin locally against a mock service. ## Steps 1. Start minikube -2. Get the Kubernetes master base url `kubectl cluster-info` +2. Get the Kubernetes master base URL `kubectl cluster-info` 3. Apply manifests `kubectl apply -f dice-roller-manifests.yaml` 4. Get service account token (see below) 5. Start Backstage UI and backend diff --git a/scripts/check-docs-quality.js b/scripts/check-docs-quality.js index 3352596890..ac9db65b40 100755 --- a/scripts/check-docs-quality.js +++ b/scripts/check-docs-quality.js @@ -13,81 +13,104 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -const { execSync, spawnSync } = require('child_process'); -// eslint-disable-next-line import/no-extraneous-dependencies -const commandExists = require('command-exists'); +const { spawnSync } = require('child_process'); +const { resolve: resolvePath, join: joinPath } = require('path'); +const fs = require('fs').promises; -const inheritStdIo = { - stdio: 'inherit', -}; +const IGNORED = [ + /^ADOPTERS\.md$/, + /^OWNERS\.md$/, + /^.*[/\\]CHANGELOG\.md$/, + /^.*[/\\]api-report\.md$/, + /^docs[/\\]releases[/\\].*-changelog\.md$/, +]; -const LINT_SKIPPED_MESSAGE = - 'Skipping documentation quality check (vale not found). Install vale linter (https://docs.errata.ai/vale/install) to enable.\n'; -const LINT_ERROR_MESSAGE = `Language linter (vale) generated errors. Please check the errors and review any markdown files that you changed. - Possibly update .github/styles/vocab.txt to add new valid words.\n`; -const VALE_NOT_FOUND_MESSAGE = `Language linter (vale) was not found. Please install vale linter (https://docs.errata.ai/vale/install).\n`; +const rootDir = resolvePath(__dirname, '..'); -// Note: Make sure the script is run as `node check-docs-quality.js [FILES]` instead of `./check-docs-quality.js [FILES]` -// If the script receives arguments (file paths), the script is run exclusively on them. (e.g. when run via pre-commit hook) -const getFilesToLint = () => { - // Files have been provided as arguments - if (process.argv.length > 2) { - return process.argv.slice(2); - } +// Manual listing to avoid dependency install for listing files in CI +async function listFiles(dir = '') { + const files = await fs.readdir(dir || rootDir); + const paths = await Promise.all( + files + .filter(file => file !== 'node_modules') + .map(async file => { + const path = joinPath(dir, file); - let command = `git ls-files | ./node_modules/.bin/shx grep ".md"`; - if (process.platform === 'win32') { - command = `git ls-files | .\\node_modules\\.bin\\shx grep ".md"`; - } + if (IGNORED.some(pattern => pattern.test(path))) { + return []; + } + if ((await fs.stat(path)).isDirectory()) { + return listFiles(path); + } + if (!path.endsWith('.md')) { + return []; + } + return path; + }), + ); + return paths.flat(); +} - // Note this ignore list only applies locally, CI runs `.github/workflows/docs-quality-checker.yml` - const ignored = ['', 'ADOPTERS.md', 'OWNERS.md']; - - return execSync(command, { - stdio: ['ignore', 'pipe', 'inherit'], - }) - .toString() - .split('\n') - .filter(el => !ignored.includes(el)); -}; - -// Proceed with the script only if Vale linter is installed. Limit the friction and surprises caused by the script. -// On CI, we want to ensure vale linter is run. -commandExists('vale') - .catch(() => { +// Proceed with the script only if Vale linter is installed. Limit the friction and surprises +// caused by the script. In CI, we want to ensure vale linter is run. +async function exitIfMissingVale() { + try { + await require('command-exists')('vale'); + } catch (e) { if (process.env.CI) { - console.log(VALE_NOT_FOUND_MESSAGE); + console.log( + `Language linter (vale) was not found. Please install vale linter (https://docs.errata.ai/vale/install).\n`, + ); process.exit(1); } - console.log(LINT_SKIPPED_MESSAGE); + console.log(`Language linter (vale) generated errors. Please check the errors and review any markdown files that you changed. + Possibly update .github/vale/Vocab/Backstage/accept.txt to add new valid words.\n`); process.exit(0); - }) - .then(() => { - const filesToLint = getFilesToLint(); + } +} - if (process.platform === 'win32') { - // Windows - try { - const output = spawnSync('vale', filesToLint, inheritStdIo); +async function runVale(files) { + const result = spawnSync( + 'vale', + ['--config', resolvePath(rootDir, '.github/vale/config.ini'), ...files], + { + stdio: 'inherit', + }, + ); - // 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.log(LINT_ERROR_MESSAGE); - } - process.exit(1); - } - } catch (e) { - console.log(e.message); - process.exit(1); - } - } else { - // Unix - const output = spawnSync('vale', filesToLint, inheritStdIo); - if (output.status !== 0) { - console.log(LINT_ERROR_MESSAGE); - process.exit(1); - } + if (result.status !== 0) { + // TODO(Rugvip): This logic was here before but seems a bit odd, could use some verification on windows. + // If it contains system level error. In this case vale does not exist. + if (process.platform !== 'win32' || result.error) { + console.log(`Language linter (vale) generated errors. Please check the errors and review any markdown files that you changed. + Possibly update .github/vale/Vocab/Backstage/accept.txt to add new valid words.\n`); } - }); + return false; + } + + return true; +} + +async function main() { + const files = await listFiles(); + + if (process.argv.includes('--ci-args')) { + process.stdout.write( + // Workaround for not being able to pass arguments to the vale action + JSON.stringify(['--config=.github/vale/config.ini', ...files]), + ); + return; + } + + await exitIfMissingVale(); + + const success = await runVale(files); + if (!success) { + process.exit(2); + } +} + +main().catch(error => { + console.error(error); + process.exit(1); +});