From 665d118422bf3d61e453bf566f0a3052187cf134 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 14:05:23 -0500 Subject: [PATCH 01/70] feat(openapi-tooling): add breaking changes checks to the verify command Signed-off-by: aramissennyeydd --- packages/repo-tools/cli-report.md | 3 +- packages/repo-tools/src/commands/index.ts | 6 ++- .../commands/repo/schema/openapi/verify.ts | 37 +++++++++++++++++-- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 9d37ce7aff..7c7dd445bc 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -168,7 +168,7 @@ Options: -h, --help Commands: - verify [paths...] + verify [options] [paths...] lint [options] [paths...] test [options] [paths...] fuzz [options] @@ -211,6 +211,7 @@ Options: Usage: backstage-repo-tools repo schema openapi verify [options] [paths...] Options: + --from -h, --help ``` diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index d2fe507941..9f0461e7ea 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -96,7 +96,11 @@ function registerRepoCommand(program: Command) { openApiCommand .command('verify [paths...]') .description( - 'Verify that all OpenAPI schemas are valid and have a matching `schemas/openapi.generated.ts` file.', + 'Verify that all OpenAPI schemas are valid and set up correctly. This also verifies that your API has not changed in a breaking way.', + ) + .option( + '--from ', + 'The base ref to compare against. Defaults to the fork point of the current branch.', ) .action( lazy(() => diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts index 442494610c..78b5a966c3 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts @@ -29,8 +29,10 @@ import { YAML_SCHEMA_PATH, } from '../../../../lib/openapi/constants'; import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; +import { exec } from '../../../../lib/exec'; +import { OptionValues } from 'commander'; -async function verify(directoryPath: string) { +async function verify(directoryPath: string, options: OptionValues) { let openapiPath = ''; try { openapiPath = await getPathToOpenApiSpec(directoryPath); @@ -58,10 +60,39 @@ async function verify(directoryPath: string) { `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools package schema openapi generate\` from '${path}' to regenerate \`${TS_SCHEMA_PATH}\`.`, ); } + + let baseRef = options.from ?? process.env.GITHUB_BASE_REF; + if (!baseRef) { + const { stdout: branch } = await exec('git merge-base --fork-point HEAD'); + baseRef = branch.toString().trim(); + } + + try { + const { stdout } = await exec('optic diff', [ + openapiPath, + '--check', + '--base', + baseRef, + ]); + // Log out the results as this still shows API changes that aren't breakages. + console.log( + stdout + .toString() + .split('\n') + .filter(e => !e.startsWith('Rerun') && e.trim()) + .join('\n'), + ); + } catch (err) { + err.message = err.stdout; + throw err; + } } -export async function bulkCommand(paths: string[] = []): Promise { - const resultsList = await runner(paths, dir => verify(dir)); +export async function bulkCommand( + paths: string[] = [], + options: OptionValues, +): Promise { + const resultsList = await runner(paths, dir => verify(dir, options)); let failed = false; for (const { relativeDir, resultText } of resultsList) { From 0b3fac608a9cb8052ad6644380add93c8116b72f Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 14:05:30 -0500 Subject: [PATCH 02/70] example breakage Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- plugins/catalog-backend/src/schema/openapi.generated.ts | 2 +- plugins/catalog-backend/src/schema/openapi.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 550db58f2e..c8c9c73a84 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1072,7 +1072,7 @@ export const spec = { 'application/json': { schema: { type: 'object', - required: ['entityRefs'], + required: ['entityRefs', 'fields'], properties: { entityRefs: { type: 'array', diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 75dc1d9bbc..d3a65490bf 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -841,6 +841,7 @@ paths: type: object required: - entityRefs + - fields properties: entityRefs: type: array From 7cd15860dc065afd44d1d56e4eb498f7093b7693 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:13:03 -0500 Subject: [PATCH 03/70] testing using the uffizzi workflow Signed-off-by: aramissennyeydd --- .../api-breaking-changes-comment.yml | 111 ++++++++ .github/workflows/api-breaking-changes.yml | 62 +++++ NOTICE | 1 + packages/repo-tools/package.json | 1 + packages/repo-tools/src/commands/index.ts | 28 +- .../commands/package/schema/openapi/check.ts | 93 +++++++ .../src/commands/repo/schema/openapi/check.ts | 80 ++++++ .../commands/repo/schema/openapi/verify.ts | 56 ++-- .../src/lib/openapi/optic/helpers.ts | 244 ++++++++++++++++++ plugins/catalog-backend/package.json | 1 + yarn.lock | 36 +++ 11 files changed, 670 insertions(+), 43 deletions(-) create mode 100644 .github/workflows/api-breaking-changes-comment.yml create mode 100644 .github/workflows/api-breaking-changes.yml create mode 100644 packages/repo-tools/src/commands/package/schema/openapi/check.ts create mode 100644 packages/repo-tools/src/commands/repo/schema/openapi/check.ts create mode 100644 packages/repo-tools/src/lib/openapi/optic/helpers.ts diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml new file mode 100644 index 0000000000..91741bba5b --- /dev/null +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -0,0 +1,111 @@ +name: API Breaking Changes (comment) + +on: + workflow_run: + workflows: + - 'API Breaking Changes (Trigger)' + types: + - completed + +jobs: + setup: + name: Add values from previous step + runs-on: ubuntu-latest + if: ${{ github.event.workflow_run.conclusion == 'success' }} + permissions: + # "If you specify the access for any of these scopes, all of those that are not specified are set to none." + # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions + actions: read # Access cache + outputs: + git-ref: ${{ steps.event.outputs.GIT_REF }} + pr-number: ${{ steps.event.outputs.PR_NUMBER }} + action: ${{ steps.event.outputs.ACTION }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + with: + disable-sudo: true + egress-policy: block + allowed-endpoints: > + api.github.com:443 + + - name: 'Download artifacts' + # Fetch output (zip archive) from the workflow run that triggered this workflow. + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({ + owner: context.repo.owner, + repo: context.repo.repo, + run_id: context.payload.workflow_run.id, + }); + let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => { + return artifact.name == "preview-spec" + })[0]; + if (matchArtifact === undefined) { + throw TypeError('Build Artifact not found!'); + } + let download = await github.rest.actions.downloadArtifact({ + owner: context.repo.owner, + repo: context.repo.repo, + artifact_id: matchArtifact.id, + archive_format: 'zip', + }); + let fs = require('fs'); + fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data)); + + - name: 'Accept event from first stage' + run: unzip preview-spec.zip event.json + + - name: Read Event into ENV + id: event + run: | + echo PR_NUMBER=$(jq '.number | tonumber' < event.json) >> $GITHUB_OUTPUT + echo ACTION=$(jq --raw-output '.action | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT + echo GIT_REF=$(jq --raw-output '.pull_request.head.sha | tostring | [scan("\\w+")][0]' < event.json) >> $GITHUB_OUTPUT + + - name: DEBUG - Print Job Outputs + if: ${{ runner.debug }} + run: | + echo "PR number: ${{ steps.event.outputs.PR_NUMBER }}" + echo "Git Ref: ${{ steps.event.outputs.GIT_REF }}" + echo "Action: ${{ steps.event.outputs.ACTION }}" + cat event.json + + - name: Get Comment + id: get-comment + run: | + unzip preview-spec.zip comment.md + ls + echo "MANIFESTS_FILE_HASH=$(md5sum manifests.rendered.yml | awk '{ print $1 }')" >> $GITHUB_OUTPUT + + add-comment: + name: Write comment about issues + needs: + - setup + if: ${{ github.event.workflow_run.conclusion == 'success' }} + permissions: + contents: read + pull-requests: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + + # Identify comment to be updated + - name: Find comment for Ephemeral Environment + uses: peter-evans/find-comment@d5fe37641ad8451bdd80312415672ba26c86575e # v3 + id: find-comment + with: + issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} + comment-author: 'github-actions[bot]' + body-includes: pr-changes-${{ needs.cache-manifests-file.outputs.pr-number }} + direction: last + + - name: Create or Update Comment with Deployment URL + uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 + with: + comment-id: ${{ steps.notification.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body-path: comment.md + edit-mode: replace diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml new file mode 100644 index 0000000000..ed2dcd9a0e --- /dev/null +++ b/.github/workflows/api-breaking-changes.yml @@ -0,0 +1,62 @@ +name: API Breaking Changes (Trigger) +on: + pull_request: + types: [opened, synchronize, reopened, closed] + paths-ignore: + - '.changeset/**' + - 'contrib/**' + - 'docs/**' + - 'microsite/**' + - 'beps/**' + - 'scripts/**' + - 'storybook/**' + - '**/*.test.*' + - '**/package.json' + - '*.md' + +jobs: + get-backstage-changes: + env: + NODE_OPTIONS: --max-old-space-size=4096 + name: Build PR image + runs-on: ubuntu-latest + if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }} + outputs: + tags: ${{ steps.meta.outputs.tags }} + steps: + - name: Harden Runner + uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 + with: + egress-policy: audit + + - name: checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: setup-node + uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 + with: + node-version: 18.x + registry-url: https://registry.npmjs.org/ + + - name: yarn install + uses: backstage/actions/yarn-install@a674369920067381b450d398b27df7039b7ef635 # v0.6.5 + with: + cache-prefix: linux-v18 + + - name: breaking changes check + run: | + yarn backstage-repo-tools repo schema openapi check > comment.md + + - name: Upload Rendered Comment as Artifact + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 + with: + name: preview-spec + path: comment.md + retention-days: 2 + + - name: Upload PR Event as Artifact + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 + with: + name: preview-spec + path: ${{ github.event_path }} + retention-days: 2 diff --git a/NOTICE b/NOTICE index fb23d28ebc..1f1dfbccbb 100644 --- a/NOTICE +++ b/NOTICE @@ -5,3 +5,4 @@ Portions of this software were developed by third-party software vendors: - Tech Radar Plugin (https://opensource.zalando.com/tech-radar/), Copyright (c) 2017 Zalando SE - [OpenAPI Generator Templates](./packages/repo-tools/templates), Copyright 2018 OpenAPI-Generator Contributors (https://openapi-generator.tech) Copyright 2018 SmartBear Software +- Optic CLI (https://github.com/opticdev/optic), Copyright 2022, Optic Labs Corporation diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index abf134b5be..126a392265 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -49,6 +49,7 @@ "@stoplight/spectral-rulesets": "^1.18.0", "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^14.0.0", + "@useoptic/openapi-utilities": "^0.54.8", "chalk": "^4.0.0", "codeowners-utils": "^1.0.2", "command-exists": "^1.2.9", diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 9f0461e7ea..6a557d909e 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -78,6 +78,14 @@ function registerPackageCommand(program: Command) { .action( lazy(() => import('./package/schema/openapi/fuzz').then(m => m.command)), ); + + openApiCommand + .command('check') + .option('--ignore', 'Ignore linting failures and only log the results.') + .option('--json', 'Output the results as JSON') + .action( + lazy(() => import('./package/schema/openapi/check').then(m => m.command)), + ); } function registerRepoCommand(program: Command) { @@ -96,11 +104,7 @@ function registerRepoCommand(program: Command) { openApiCommand .command('verify [paths...]') .description( - 'Verify that all OpenAPI schemas are valid and set up correctly. This also verifies that your API has not changed in a breaking way.', - ) - .option( - '--from ', - 'The base ref to compare against. Defaults to the fork point of the current branch.', + 'Verify that all OpenAPI schemas are valid and set up correctly.', ) .action( lazy(() => @@ -137,6 +141,20 @@ function registerRepoCommand(program: Command) { .action( lazy(() => import('./repo/schema/openapi/fuzz').then(m => m.command)), ); + + openApiCommand + .command('check') + .description( + 'Check the repository against a specific ref, will run all package `check:api` scripts.', + ) + .option( + '--since ', + 'Check the API against a specific ref', + 'origin/master', + ) + .action( + lazy(() => import('./repo/schema/openapi/check').then(m => m.command)), + ); } export function registerCommands(program: Command) { diff --git a/packages/repo-tools/src/commands/package/schema/openapi/check.ts b/packages/repo-tools/src/commands/package/schema/openapi/check.ts new file mode 100644 index 0000000000..22d2d0a0d6 --- /dev/null +++ b/packages/repo-tools/src/commands/package/schema/openapi/check.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2023 The Backstage Authors + * + * 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. + */ +import chalk from 'chalk'; +import { exec } from '../../../../lib/exec'; +import { getPathToCurrentOpenApiSpec } from '../../../../lib/openapi/helpers'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { OptionValues } from 'commander'; +import { env } from 'process'; +import { readFile, rm } from 'fs/promises'; +import { resolve } from 'path'; + +const reduceOpticOutput = (output: string) => { + return output + .split('\n') + .filter(e => !e.startsWith('Rerun') && e.trim()) + .join('\n'); +}; + +async function check(opts: OptionValues) { + const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec(); + + let baseRef = opts.since ?? process.env.GITHUB_BASE_REF; + if (!baseRef) { + const { stdout: branch } = await exec( + 'git merge-base --fork-point origin/master', + ); + baseRef = branch.toString().trim(); + } + + let failed = false; + let output = ''; + try { + const { stdout } = await exec( + 'yarn optic diff', + [ + resolvedOpenapiPath, + '--check', + opts.json ? '--json' : '', + '--base', + baseRef, + ], + { + cwd: cliPaths.targetRoot, + env: { CI: opts.json ? '1' : undefined, ...env }, + }, + ); + output = stdout.toString(); + } catch (err) { + output = err.stdout; + failed = true; + } + + if (opts.json) { + const file = ( + await readFile(resolve(cliPaths.targetRoot, 'ci-run-details.json')) + ).toString(); + const results = JSON.parse(file); + console.log(file); + if (!opts.ignore && results.failed) { + throw new Error('Some checks failed'); + } + + await rm(resolve(cliPaths.targetRoot, 'ci-run-details.json')); + } else { + console.log(reduceOpticOutput(output)); + if (!opts.ignore && failed) { + throw new Error('Some checks failed'); + } + } +} + +export async function command(opts: OptionValues) { + try { + await check(opts); + if (!opts.json) console.log(chalk.green(`All checks passed.`)); + } catch (err) { + if (!opts.json) console.log(chalk.red(err.message)); + process.exit(1); + } +} diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts new file mode 100644 index 0000000000..f77586a840 --- /dev/null +++ b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts @@ -0,0 +1,80 @@ +/* + * Copyright 2024 The Backstage Authors + * + * 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. + */ +import { PackageGraph } from '@backstage/cli-node'; +import { OptionValues } from 'commander'; +import { exec } from '../../../../lib/exec'; +import { + CiRunDetails, + generateCompareSummaryMarkdown, +} from '../../../../lib/openapi/optic/helpers'; + +export async function command(opts: OptionValues) { + let packages = await PackageGraph.listTargetPackages(); + if (opts.since) { + const graph = PackageGraph.fromPackages(packages); + const changedPackages = await graph.listChangedPackages({ + ref: opts.since, + analyzeLockfile: true, + }); + const withDevDependents = graph.collectPackageNames( + changedPackages.map(pkg => pkg.name), + pkg => pkg.localDevDependents.keys(), + ); + packages = Array.from(withDevDependents).map(name => graph.get(name)!); + } + + const checkablePackages = packages.filter( + e => e.packageJson.scripts?.['check:api'], + ); + try { + const outputs = { + completed: [], + failed: [], + noop: [], + severity: 0, + } as CiRunDetails; + for (const pkg of checkablePackages) { + const { stdout } = await exec( + 'yarn', + ['check:api', '--ignore', '--json'], + { + cwd: pkg.dir, + }, + ); + const result = JSON.parse(stdout.toString()); + outputs.completed.push(...(result.completed ?? [])); + outputs.failed.push(...(result.failed ?? [])); + outputs.noop.push(...(result.noop ?? [])); + } + + const { stdout: currentSha } = await exec('git', ['rev-parse', 'HEAD']); + console.log( + generateCompareSummaryMarkdown( + { sha: currentSha.toString().trim() }, + outputs, + { verbose: true }, + ), + ); + + const failed = outputs.failed.length > 0; + if (failed) { + throw new Error('Some checks failed'); + } + } catch (err) { + console.error(err); + process.exit(1); + } +} diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts index 78b5a966c3..2902d99ea4 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts @@ -29,17 +29,14 @@ import { YAML_SCHEMA_PATH, } from '../../../../lib/openapi/constants'; import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; -import { exec } from '../../../../lib/exec'; -import { OptionValues } from 'commander'; -async function verify(directoryPath: string, options: OptionValues) { - let openapiPath = ''; - try { - openapiPath = await getPathToOpenApiSpec(directoryPath); - } catch { - // Unable to find spec at path. - return; - } +const verifySpecAndGeneratedSpecMatch = async ( + openapiPath: string, + directoryPath: string, +) => { + const openapiTempDirectory = resolvePath(cliPaths.targetDir, '.openapi'); + await fs.mkdirp(openapiTempDirectory); + console.log(openapiTempDirectory); const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8')); await Parser.validate(cloneDeep(yaml) as any); @@ -60,39 +57,22 @@ async function verify(directoryPath: string, options: OptionValues) { `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools package schema openapi generate\` from '${path}' to regenerate \`${TS_SCHEMA_PATH}\`.`, ); } +}; - let baseRef = options.from ?? process.env.GITHUB_BASE_REF; - if (!baseRef) { - const { stdout: branch } = await exec('git merge-base --fork-point HEAD'); - baseRef = branch.toString().trim(); - } - +async function verify(directoryPath: string) { + let openapiPath = ''; try { - const { stdout } = await exec('optic diff', [ - openapiPath, - '--check', - '--base', - baseRef, - ]); - // Log out the results as this still shows API changes that aren't breakages. - console.log( - stdout - .toString() - .split('\n') - .filter(e => !e.startsWith('Rerun') && e.trim()) - .join('\n'), - ); - } catch (err) { - err.message = err.stdout; - throw err; + openapiPath = await getPathToOpenApiSpec(directoryPath); + } catch { + // Unable to find spec at path. + return; } + + await verifySpecAndGeneratedSpecMatch(openapiPath, directoryPath); } -export async function bulkCommand( - paths: string[] = [], - options: OptionValues, -): Promise { - const resultsList = await runner(paths, dir => verify(dir, options)); +export async function bulkCommand(paths: string[] = []): Promise { + const resultsList = await runner(paths, dir => verify(dir)); let failed = false; for (const { relativeDir, resultText } of resultsList) { diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts new file mode 100644 index 0000000000..4c50f9bbd9 --- /dev/null +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -0,0 +1,244 @@ +/* + * Copyright 2024 The Backstage Authors + * + * 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. + */ +/* eslint-disable no-nested-ternary */ + +import { + compareSpecs, + groupDiffsByEndpoint, + Severity, + getOperationsChangedLabel, + getOperationsChanged, +} from '@useoptic/openapi-utilities'; +import { GroupedDiffs } from '@useoptic/openapi-utilities/build/openapi3/group-diff'; +import { relative } from 'path'; +import { paths as cliPaths } from '../../paths'; + +type Comparison = { + groupedDiffs: ReturnType; + results: Awaited>['results']; +}; + +export type CiRunDetails = { + completed: { + warnings: string[]; + apiName: string; + opticWebUrl?: string | null; + comparison: Comparison; + specUrl?: string | null; + capture?: any; + }[]; + failed: { apiName: string; error: string }[]; + noop: { apiName: string }[]; + severity: Severity; +}; + +const getChecksLabel = ( + results: CiRunDetails['completed'][number]['comparison']['results'], + severity: Severity, +) => { + const totalChecks = results.length; + let failingChecks = 0; + let exemptedFailingChecks = 0; + + for (const result of results) { + if (result.passed) continue; + if (result.severity < severity) continue; + if (result.exempted) exemptedFailingChecks += 1; + else failingChecks += 1; + } + + const exemptedChunk = + exemptedFailingChecks > 0 ? `, ${exemptedFailingChecks} exempted` : ''; + + return failingChecks > 0 + ? `⚠️ **${failingChecks}**/**${totalChecks}** failed${exemptedChunk}` + : totalChecks > 0 + ? `✅ **${totalChecks}** passed${exemptedChunk}` + : `ℹ️ No automated checks have run`; +}; + +function getOperationsText( + groupedDiffs: GroupedDiffs, + options: { webUrl?: string | null; verbose: boolean; labelJoiner?: string }, +) { + const ops = getOperationsChanged(groupedDiffs); + + const operationsText = options.verbose + ? [ + ...[...ops.added].map(o => `\`${o}\` (added)`), + ...[...ops.changed].map(o => `\`${o}\` (changed)`), + ...[...ops.removed].map(o => `\`${o}\` (removed)`), + ].join('\n') + : ''; + return `${getOperationsChangedLabel(groupedDiffs, { + joiner: options.labelJoiner, + })} + + ${operationsText} + `; +} + +const getCaptureIssuesLabel = ({ + unmatchedInteractions, + mismatchedEndpoints, +}: { + unmatchedInteractions: number; + mismatchedEndpoints: number; +}) => { + return [ + ...(unmatchedInteractions + ? [ + `🆕 ${unmatchedInteractions} undocumented path${ + unmatchedInteractions > 1 ? 's' : '' + }`, + ] + : []), + ...(mismatchedEndpoints + ? [ + `⚠️ ${mismatchedEndpoints} mismatch${ + mismatchedEndpoints > 1 ? 'es' : '' + }`, + ] + : []), + ].join('\n'); +}; + +export const generateCompareSummaryMarkdown = ( + commit: { sha: string }, + results: CiRunDetails, + options: { verbose: boolean }, +) => { + const anyCompletedHasWarning = results.completed.some( + s => s.warnings.length > 0, + ); + const anyCompletedHasCapture = results.completed.some(s => s.capture); + return ` + ${ + results.completed.length > 0 + ? `### APIs Changed + + + + + + + + ${anyCompletedHasWarning ? '' : ''} + ${anyCompletedHasCapture ? '' : ''} + + + + + ${results.completed + .map( + s => + ` + + + + + ${anyCompletedHasWarning ? `` : ''} + + ${ + anyCompletedHasCapture + ? ` + + + + ` + : '' + } + `, + ) + .join('\n')} + +
APIChangesRulesWarningsTests
+ + ${relative(cliPaths.targetDir, s.apiName)} + + + + ${getOperationsText(s.comparison.groupedDiffs, { + webUrl: s.opticWebUrl, + verbose: options.verbose, + labelJoiner: ',\n', + })} + + + + ${getChecksLabel(s.comparison.results, results.severity)} + + ${s.warnings.join('\n')} + + ${ + s.capture + ? s.capture.success + ? s.capture.mismatchedEndpoints || s.capture.unmatchedInteractions + ? getCaptureIssuesLabel({ + unmatchedInteractions: s.capture.unmatchedInteractions, + mismatchedEndpoints: s.capture.mismatchedEndpoints, + }) + : `✅ ${s.capture.percentCovered}% coverage` + : '❌ Failed to run' + : '' + } + +
+ ` + : '' + } + ${ + results.failed.length > 0 + ? `### Errors running optic + + + + + + + + + + ${results.failed + .map( + s => ` + + + `, + ) + .join('\n')} + +
APIError
${s.apiName} + + ${'```'} + ${s.error} + ${'```'} + +
+ ` + : '' + } + + Summary of API changes for commit (${commit.sha}) + + ${ + results.noop.length > 0 + ? `${ + results.noop.length === 1 ? '1 API' : `${results.noop.length} APIs` + } had no changes.` + : '' + }`; +}; diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 0a20041ee4..058b9318d7 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -42,6 +42,7 @@ ], "scripts": { "build": "backstage-cli package build", + "check:api": "backstage-repo-tools package schema openapi check", "clean": "backstage-cli package clean", "fuzz": "backstage-repo-tools package schema openapi fuzz --exclude-checks response_schema_conformance", "generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/catalog-client", diff --git a/yarn.lock b/yarn.lock index b7a8b0ce2c..b6a138814b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10108,6 +10108,7 @@ __metadata: "@types/is-glob": ^4.0.2 "@types/node": ^18.17.8 "@types/prettier": ^2.0.0 + "@useoptic/openapi-utilities": ^0.54.8 chalk: ^4.0.0 codeowners-utils: ^1.0.2 command-exists: ^1.2.9 @@ -20292,6 +20293,16 @@ __metadata: languageName: node linkType: hard +"@useoptic/json-pointer-helpers@npm:0.54.8": + version: 0.54.8 + resolution: "@useoptic/json-pointer-helpers@npm:0.54.8" + dependencies: + jsonpointer: ^5.0.1 + minimatch: 9.0.3 + checksum: 4eddabb6dce3ca8160dcd4904299b6964945c3fe47d39bfeca6c68b9a50b058b901a6fb10ab168295475d651df3349149faa5f27f77293e15b6eee8d4417432e + languageName: node + linkType: hard + "@useoptic/openapi-io@npm:0.50.10": version: 0.50.10 resolution: "@useoptic/openapi-io@npm:0.50.10" @@ -20346,6 +20357,31 @@ __metadata: languageName: node linkType: hard +"@useoptic/openapi-utilities@npm:^0.54.8": + version: 0.54.8 + resolution: "@useoptic/openapi-utilities@npm:0.54.8" + dependencies: + "@useoptic/json-pointer-helpers": 0.54.8 + ajv: ^8.6.0 + ajv-errors: ~3.0.0 + ajv-formats: ~2.1.0 + chalk: ^4.1.2 + fast-deep-equal: ^3.1.3 + is-url: ^1.2.4 + js-yaml: ^4.1.0 + json-stable-stringify: ^1.0.1 + lodash.groupby: ^4.6.0 + lodash.isequal: ^4.5.0 + lodash.omit: ^4.5.0 + node-machine-id: ^1.1.12 + openapi-types: ^12.0.2 + ts-invariant: ^0.9.3 + url-join: ^4.0.1 + yaml-ast-parser: ^0.0.43 + checksum: fa9e9f430c77687591aaf8b43b7b31a7c2f80fe9c140aaa978f1948f84d3e974181c91c3d8ec3e06efca9735c7826290baf4be72063bf733887aa632b40c3c4a + languageName: node + linkType: hard + "@useoptic/optic@npm:^0.50.10": version: 0.50.10 resolution: "@useoptic/optic@npm:0.50.10" From 7b06c6c78fbd398b1f70b5986448beb810e7ad3c Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:14:17 -0500 Subject: [PATCH 04/70] add attribution comment as well Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- packages/repo-tools/src/lib/openapi/optic/helpers.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts index 4c50f9bbd9..cd2a1637d4 100644 --- a/packages/repo-tools/src/lib/openapi/optic/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -26,6 +26,11 @@ import { GroupedDiffs } from '@useoptic/openapi-utilities/build/openapi3/group-d import { relative } from 'path'; import { paths as cliPaths } from '../../paths'; +/** + * The below code is copied from https://github.com/opticdev/optic/blob/main/projects/optic/src/commands/ci/comment/common.ts#L82 for use + * with a security flow for forked repositories. + */ + type Comparison = { groupedDiffs: ReturnType; results: Awaited>['results']; From 53c1ec25e221b8d0bd4fb0ded8412f7788938315 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:14:52 -0500 Subject: [PATCH 05/70] update paths requirement Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .github/workflows/api-breaking-changes.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index ed2dcd9a0e..c6227792c1 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -2,17 +2,8 @@ name: API Breaking Changes (Trigger) on: pull_request: types: [opened, synchronize, reopened, closed] - paths-ignore: - - '.changeset/**' - - 'contrib/**' - - 'docs/**' - - 'microsite/**' - - 'beps/**' - - 'scripts/**' - - 'storybook/**' - - '**/*.test.*' - - '**/package.json' - - '*.md' + paths: + - '**/openapi.yaml' jobs: get-backstage-changes: From fe4c26532bee4514f4cb171780cb4e9333b4fea9 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:22:00 -0500 Subject: [PATCH 06/70] update git command for GA env Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- packages/repo-tools/src/commands/index.ts | 1 + .../repo-tools/src/commands/package/schema/openapi/check.ts | 2 +- packages/repo-tools/src/commands/repo/schema/openapi/check.ts | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 6a557d909e..3fa16aa512 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -83,6 +83,7 @@ function registerPackageCommand(program: Command) { .command('check') .option('--ignore', 'Ignore linting failures and only log the results.') .option('--json', 'Output the results as JSON') + .option('--since ', 'Check the API against a specific ref') .action( lazy(() => import('./package/schema/openapi/check').then(m => m.command)), ); diff --git a/packages/repo-tools/src/commands/package/schema/openapi/check.ts b/packages/repo-tools/src/commands/package/schema/openapi/check.ts index 22d2d0a0d6..3cb0baf313 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/check.ts @@ -32,7 +32,7 @@ const reduceOpticOutput = (output: string) => { async function check(opts: OptionValues) { const resolvedOpenapiPath = await getPathToCurrentOpenApiSpec(); - let baseRef = opts.since ?? process.env.GITHUB_BASE_REF; + let baseRef = opts.since; if (!baseRef) { const { stdout: branch } = await exec( 'git merge-base --fork-point origin/master', diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts index f77586a840..41d0bec0e5 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts @@ -47,9 +47,10 @@ export async function command(opts: OptionValues) { severity: 0, } as CiRunDetails; for (const pkg of checkablePackages) { + const baseRef = opts.since ?? process.env.GITHUB_BASE_REF; const { stdout } = await exec( 'yarn', - ['check:api', '--ignore', '--json'], + ['check:api', '--ignore', '--json', '--since', baseRef], { cwd: pkg.dir, }, From b0b1371075e58308dcc7aaef45c6aa8809d12163 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:28:04 -0500 Subject: [PATCH 07/70] use base ref instead Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .github/workflows/api-breaking-changes.yml | 2 +- packages/repo-tools/src/commands/repo/schema/openapi/check.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index c6227792c1..fbe4764a9f 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -36,7 +36,7 @@ jobs: - name: breaking changes check run: | - yarn backstage-repo-tools repo schema openapi check > comment.md + yarn backstage-repo-tools repo schema openapi check --since ${{ github.base_ref }} > comment.md - name: Upload Rendered Comment as Artifact uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts index 41d0bec0e5..492dd3b5be 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts @@ -47,7 +47,7 @@ export async function command(opts: OptionValues) { severity: 0, } as CiRunDetails; for (const pkg of checkablePackages) { - const baseRef = opts.since ?? process.env.GITHUB_BASE_REF; + const baseRef = opts.since; const { stdout } = await exec( 'yarn', ['check:api', '--ignore', '--json', '--since', baseRef], From d1b44c9a4af18c380aa0d26884209250c25d0952 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:34:22 -0500 Subject: [PATCH 08/70] get the actual sha and run with that Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .../repo-tools/src/commands/repo/schema/openapi/check.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts index 492dd3b5be..5599937322 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts @@ -23,7 +23,11 @@ import { export async function command(opts: OptionValues) { let packages = await PackageGraph.listTargetPackages(); + + let since = ''; if (opts.since) { + const { stdout: sinceRaw } = await exec('git', ['rev-parse', opts.since]); + since = sinceRaw.toString().trim(); const graph = PackageGraph.fromPackages(packages); const changedPackages = await graph.listChangedPackages({ ref: opts.since, @@ -47,10 +51,10 @@ export async function command(opts: OptionValues) { severity: 0, } as CiRunDetails; for (const pkg of checkablePackages) { - const baseRef = opts.since; + const sinceCommands = since ? ['--since', since] : []; const { stdout } = await exec( 'yarn', - ['check:api', '--ignore', '--json', '--since', baseRef], + ['check:api', '--ignore', '--json', ...sinceCommands], { cwd: pkg.dir, }, From dce3d7870dd468a4e7e7daede7e915e1429a4e8f Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:38:23 -0500 Subject: [PATCH 09/70] actually check out the needed branches Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .github/workflows/api-breaking-changes.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index fbe4764a9f..406205d578 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -20,8 +20,13 @@ jobs: with: egress-policy: audit - - name: checkout - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + # Fetch the commit that's merged into the base rather than the target ref + # This will let us diff only the contents of the PR, without fetching more history + ref: 'refs/pull/${{ github.event.pull_request.number }}/merge' + - name: fetch base + run: git fetch --depth 1 origin ${{ github.base_ref }} - name: setup-node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 From e2c9b91fe899feee8308756cafc25af2c18327dd Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:41:06 -0500 Subject: [PATCH 10/70] add origin to base ref Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .github/workflows/api-breaking-changes.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 406205d578..5c75910546 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -41,7 +41,7 @@ jobs: - name: breaking changes check run: | - yarn backstage-repo-tools repo schema openapi check --since ${{ github.base_ref }} > comment.md + yarn backstage-repo-tools repo schema openapi check --since origin/${{ github.base_ref }} > comment.md - name: Upload Rendered Comment as Artifact uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3 From 8f681584e8f4b6cbc91732885f322823c819c6b6 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 16:48:23 -0500 Subject: [PATCH 11/70] update comment to show correctly Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .../src/lib/openapi/optic/helpers.ts | 219 +++++++++--------- 1 file changed, 104 insertions(+), 115 deletions(-) diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts index cd2a1637d4..9bcd8c45a3 100644 --- a/packages/repo-tools/src/lib/openapi/optic/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -131,119 +131,108 @@ export const generateCompareSummaryMarkdown = ( ); const anyCompletedHasCapture = results.completed.some(s => s.capture); return ` - ${ - results.completed.length > 0 - ? `### APIs Changed - - - - - - - - ${anyCompletedHasWarning ? '' : ''} - ${anyCompletedHasCapture ? '' : ''} - - - - - ${results.completed - .map( - s => - ` - - - - - ${anyCompletedHasWarning ? `` : ''} - - ${ - anyCompletedHasCapture - ? ` - - - - ` - : '' - } - `, - ) - .join('\n')} - -
APIChangesRulesWarningsTests
- - ${relative(cliPaths.targetDir, s.apiName)} - - - - ${getOperationsText(s.comparison.groupedDiffs, { - webUrl: s.opticWebUrl, - verbose: options.verbose, - labelJoiner: ',\n', - })} - - - - ${getChecksLabel(s.comparison.results, results.severity)} - - ${s.warnings.join('\n')} - - ${ - s.capture - ? s.capture.success - ? s.capture.mismatchedEndpoints || s.capture.unmatchedInteractions - ? getCaptureIssuesLabel({ - unmatchedInteractions: s.capture.unmatchedInteractions, - mismatchedEndpoints: s.capture.mismatchedEndpoints, - }) - : `✅ ${s.capture.percentCovered}% coverage` - : '❌ Failed to run' - : '' - } - -
- ` - : '' - } - ${ - results.failed.length > 0 - ? `### Errors running optic - - - - - - - - - - ${results.failed - .map( - s => ` - - - `, - ) - .join('\n')} - -
APIError
${s.apiName} - - ${'```'} - ${s.error} - ${'```'} - -
- ` - : '' - } - - Summary of API changes for commit (${commit.sha}) - - ${ - results.noop.length > 0 - ? `${ - results.noop.length === 1 ? '1 API' : `${results.noop.length} APIs` - } had no changes.` - : '' - }`; +${ + results.completed.length > 0 + ? `### APIs Changed + + + + + + + +${anyCompletedHasWarning ? '' : ''} +${anyCompletedHasCapture ? '' : ''} + + + +${results.completed + .map( + s => + ` + + + + +${anyCompletedHasWarning ? `` : ''} + +${ + anyCompletedHasCapture + ? ` + +` + : '' +} +`, + ) + .join('\n')} + +
APIChangesRulesWarningsTests
+${relative(cliPaths.targetDir, s.apiName)} + +${getOperationsText(s.comparison.groupedDiffs, { + webUrl: s.opticWebUrl, + verbose: options.verbose, + labelJoiner: ',\n', +})} + +${getChecksLabel(s.comparison.results, results.severity)} +${s.warnings.join('\n')} +${ + s.capture + ? s.capture.success + ? s.capture.mismatchedEndpoints || s.capture.unmatchedInteractions + ? getCaptureIssuesLabel({ + unmatchedInteractions: s.capture.unmatchedInteractions, + mismatchedEndpoints: s.capture.mismatchedEndpoints, + }) + : `✅ ${s.capture.percentCovered}% coverage` + : '❌ Failed to run' + : '' +} +
+` + : '' +} +${ + results.failed.length > 0 + ? `### Errors running optic + + + + + + + + + +${results.failed + .map( + s => ` + + +`, + ) + .join('\n')} + +
APIError
${s.apiName} + +${'```'} +${s.error} +${'```'} + +
+` + : '' +} + +Summary of API changes for commit (${commit.sha}) + +${ + results.noop.length > 0 + ? `${ + results.noop.length === 1 ? '1 API' : `${results.noop.length} APIs` + } had no changes.` + : '' +}`; }; From 351ae33e34b8f160d020004548291e1549a72a66 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 18:30:11 -0500 Subject: [PATCH 12/70] update workflow with improved comment structure Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .../api-breaking-changes-comment.yml | 12 +- .github/workflows/api-breaking-changes.yml | 2 - .../src/commands/repo/schema/openapi/check.ts | 62 ++++++++-- .../src/lib/openapi/optic/helpers.ts | 112 +++++++++++++++--- .../src/schema/openapi.generated.ts | 2 +- 5 files changed, 157 insertions(+), 33 deletions(-) diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml index 91741bba5b..4c7fa452e7 100644 --- a/.github/workflows/api-breaking-changes-comment.yml +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -77,7 +77,7 @@ jobs: run: | unzip preview-spec.zip comment.md ls - echo "MANIFESTS_FILE_HASH=$(md5sum manifests.rendered.yml | awk '{ print $1 }')" >> $GITHUB_OUTPUT + grep add-comment: name: Write comment about issues @@ -93,19 +93,19 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 # Identify comment to be updated - - name: Find comment for Ephemeral Environment + - name: Find comment for API Changes uses: peter-evans/find-comment@d5fe37641ad8451bdd80312415672ba26c86575e # v3 id: find-comment with: - issue-number: ${{ needs.cache-manifests-file.outputs.pr-number }} + issue-number: ${{ needs.setup.outputs.pr-number }} comment-author: 'github-actions[bot]' - body-includes: pr-changes-${{ needs.cache-manifests-file.outputs.pr-number }} + body-includes: API changes direction: last - - name: Create or Update Comment with Deployment URL + - name: Create or Update Comment with API Changes uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4 with: - comment-id: ${{ steps.notification.outputs.comment-id }} + comment-id: ${{ steps.find-comment.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body-path: comment.md edit-mode: replace diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 5c75910546..07206718bb 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -12,8 +12,6 @@ jobs: name: Build PR image runs-on: ubuntu-latest if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }} - outputs: - tags: ${{ steps.meta.outputs.tags }} steps: - name: Harden Runner uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0 diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts index 5599937322..d434e0cd4d 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts @@ -20,6 +20,8 @@ import { CiRunDetails, generateCompareSummaryMarkdown, } from '../../../../lib/openapi/optic/helpers'; +import { paths as cliPaths } from '../../../../lib/paths'; +import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; export async function command(opts: OptionValues) { let packages = await PackageGraph.listTargetPackages(); @@ -28,26 +30,34 @@ export async function command(opts: OptionValues) { if (opts.since) { const { stdout: sinceRaw } = await exec('git', ['rev-parse', opts.since]); since = sinceRaw.toString().trim(); - const graph = PackageGraph.fromPackages(packages); - const changedPackages = await graph.listChangedPackages({ - ref: opts.since, - analyzeLockfile: true, - }); - const withDevDependents = graph.collectPackageNames( - changedPackages.map(pkg => pkg.name), - pkg => pkg.localDevDependents.keys(), + const { stdout: changedFilesRaw } = await exec('git', [ + 'diff', + '--name-only', + since, + ]); + const changedFiles = changedFilesRaw.toString().trim(); + + const changedOpenApiSpecs = changedFiles + .split('\n') + .filter(e => e.endsWith(YAML_SCHEMA_PATH)) + .map(e => cliPaths.resolveTarget(e)); + + // filter packages by changedFiles + packages = packages.filter(pkg => + changedOpenApiSpecs.some(e => e.startsWith(`${pkg.dir}/`)), ); - packages = Array.from(withDevDependents).map(name => graph.get(name)!); } const checkablePackages = packages.filter( e => e.packageJson.scripts?.['check:api'], ); + try { const outputs = { completed: [], failed: [], noop: [], + warning: [], severity: 0, } as CiRunDetails; for (const pkg of checkablePackages) { @@ -65,6 +75,40 @@ export async function command(opts: OptionValues) { outputs.noop.push(...(result.noop ?? [])); } + for (const pkg of packages.filter( + e => !e.packageJson.scripts?.['check:api'], + )) { + outputs.warning?.push({ + apiName: `${pkg.dir}/`, + warning: 'No check:api script found in package.json', + }); + } + + outputs.completed.forEach( + e => + (e.apiName = e.apiName + .replace(cliPaths.targetDir, '') + .replace(YAML_SCHEMA_PATH, '')), + ); + outputs.failed.forEach( + e => + (e.apiName = e.apiName + .replace(cliPaths.targetDir, '') + .replace(YAML_SCHEMA_PATH, '')), + ); + outputs.noop.forEach( + e => + (e.apiName = e.apiName + .replace(cliPaths.targetDir, '') + .replace(YAML_SCHEMA_PATH, '')), + ); + outputs.warning?.forEach( + e => + (e.apiName = e.apiName + .replace(cliPaths.targetDir, '') + .replace(YAML_SCHEMA_PATH, '')), + ); + const { stdout: currentSha } = await exec('git', ['rev-parse', 'HEAD']); console.log( generateCompareSummaryMarkdown( diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts index 9bcd8c45a3..2393e71ce8 100644 --- a/packages/repo-tools/src/lib/openapi/optic/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -23,8 +23,6 @@ import { getOperationsChanged, } from '@useoptic/openapi-utilities'; import { GroupedDiffs } from '@useoptic/openapi-utilities/build/openapi3/group-diff'; -import { relative } from 'path'; -import { paths as cliPaths } from '../../paths'; /** * The below code is copied from https://github.com/opticdev/optic/blob/main/projects/optic/src/commands/ci/comment/common.ts#L82 for use @@ -45,6 +43,7 @@ export type CiRunDetails = { specUrl?: string | null; capture?: any; }[]; + warning?: { apiName: string; warning: string }[]; failed: { apiName: string; error: string }[]; noop: { apiName: string }[]; severity: Severity; @@ -121,6 +120,18 @@ const getCaptureIssuesLabel = ({ ].join('\n'); }; +const getBreakagesRow = (breakage: CiRunDetails['completed'][number]) => { + return ` + - ${breakage.apiName} + ${breakage.comparison.results.map( + s => ` + - ${s.where} + ${'```'} + ${s.error} + ${'```'}`, + )}`; +}; + export const generateCompareSummaryMarkdown = ( commit: { sha: string }, results: CiRunDetails, @@ -130,10 +141,61 @@ export const generateCompareSummaryMarkdown = ( s => s.warnings.length > 0, ); const anyCompletedHasCapture = results.completed.some(s => s.capture); - return ` + if ( + results.completed.length === 0 && + results.failed.length === 0 && + results.failed.length === 0 + ) { + return `No API changes detected for commit (${commit.sha})`; + } + const breakages = results.completed + .filter(s => s.comparison.results.some(e => !e.passed)) + .map(e => ({ + ...e, + comparison: { + ...e.comparison, + results: e.comparison.results.filter(f => !f.passed), + }, + })); + const successfullyCompletedCount = + results.completed.length - breakages.length; + return `### Summary for commit (${commit.sha}) + +${ + results.noop.length > 0 + ? `${ + results.noop.length === 1 ? '1 API' : `${results.noop.length} APIs` + } had no changes.` + : '' +} +${ + breakages.length > 0 + ? `${ + breakages.length === 1 ? '1 API' : `${breakages.length} APIs` + } had breaking changes.` + : '' +} +${ + successfullyCompletedCount > 0 + ? `${ + successfullyCompletedCount === 1 + ? '1 API' + : `${successfullyCompletedCount} APIs` + } had non-breaking changes.` + : '' +} +${ + results.warning && results.warning.length > 0 + ? `${ + results.warning.length === 1 + ? '1 API' + : `${results.warning.length} APIs` + } had warnings.` + : '' +} ${ results.completed.length > 0 - ? `### APIs Changed + ? `### APIs with Changes @@ -151,7 +213,7 @@ ${results.completed s => `
-${relative(cliPaths.targetDir, s.apiName)} +${s.apiName} ${getOperationsText(s.comparison.groupedDiffs, { @@ -190,13 +252,13 @@ ${ ) .join('\n')} -
-` +` : '' } + ${ results.failed.length > 0 - ? `### Errors running optic + ? `### APIs with Errors @@ -226,13 +288,33 @@ ${'```'} : '' } -Summary of API changes for commit (${commit.sha}) - ${ - results.noop.length > 0 - ? `${ - results.noop.length === 1 ? '1 API' : `${results.noop.length} APIs` - } had no changes.` + results.warning && results.warning.length + ? ` +### APIs with Warnings +
+ + + + + + + + ${results.warning + .map(e => ``) + .join('\n')} + +
APIWarning
${e.apiName}${e.warning}
` : '' -}`; +} +${ + breakages.length > 0 + ? ` +### Routes with Breakages + +${breakages.map(getBreakagesRow).join('\n')} +` + : '' +} +`; }; diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index c8c9c73a84..550db58f2e 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1072,7 +1072,7 @@ export const spec = { 'application/json': { schema: { type: 'object', - required: ['entityRefs', 'fields'], + required: ['entityRefs'], properties: { entityRefs: { type: 'array', From 683870a29ada01c4971a5a6efa4ac3c3fe330bc6 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 18:35:09 -0500 Subject: [PATCH 13/70] add changeset Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .changeset/flat-countries-clap.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/flat-countries-clap.md diff --git a/.changeset/flat-countries-clap.md b/.changeset/flat-countries-clap.md new file mode 100644 index 0000000000..ccd779a2b5 --- /dev/null +++ b/.changeset/flat-countries-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +Adds 2 new commands `repo schema openapi check` and `package schema openapi check`. `repo schema openapi check` is intended to power a new breaking changes check on pull requests and the package level command allows plugin developers to quickly see new API breaking changes.They're intended to be used in complement with the existing `repo schema openapi verify` command to validate your OpenAPI spec against a variety of things. From d3d227d44fba792da9cdce1b184e86b137fb45dd Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 18:36:14 -0500 Subject: [PATCH 14/70] revert catalog changes Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- plugins/catalog-backend/src/schema/openapi.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index d3a65490bf..75dc1d9bbc 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -841,7 +841,6 @@ paths: type: object required: - entityRefs - - fields properties: entityRefs: type: array From 9341347dc129e53a343ebcef742a583568dec45c Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 18:36:33 -0500 Subject: [PATCH 15/70] revert prettier change Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- plugins/catalog-backend/src/schema/openapi.generated.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 550db58f2e..823ac40a14 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From f4aebb8e88012d1f9f80f63962b5a7b2ad866971 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 2 Mar 2024 18:44:06 -0500 Subject: [PATCH 16/70] small refactoring Signed-off-by: aramissennyeydd Signed-off-by: web-next-automation --- .../src/commands/repo/schema/openapi/check.ts | 34 ++++--------- .../src/lib/openapi/optic/helpers.ts | 48 +++++++------------ 2 files changed, 26 insertions(+), 56 deletions(-) diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts index d434e0cd4d..95bd4dffed 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/check.ts @@ -23,6 +23,12 @@ import { import { paths as cliPaths } from '../../../../lib/paths'; import { YAML_SCHEMA_PATH } from '../../../../lib/openapi/constants'; +function cleanUpApiName(e: { apiName: string }) { + e.apiName = e.apiName + .replace(cliPaths.targetDir, '') + .replace(YAML_SCHEMA_PATH, ''); +} + export async function command(opts: OptionValues) { let packages = await PackageGraph.listTargetPackages(); @@ -84,30 +90,10 @@ export async function command(opts: OptionValues) { }); } - outputs.completed.forEach( - e => - (e.apiName = e.apiName - .replace(cliPaths.targetDir, '') - .replace(YAML_SCHEMA_PATH, '')), - ); - outputs.failed.forEach( - e => - (e.apiName = e.apiName - .replace(cliPaths.targetDir, '') - .replace(YAML_SCHEMA_PATH, '')), - ); - outputs.noop.forEach( - e => - (e.apiName = e.apiName - .replace(cliPaths.targetDir, '') - .replace(YAML_SCHEMA_PATH, '')), - ); - outputs.warning?.forEach( - e => - (e.apiName = e.apiName - .replace(cliPaths.targetDir, '') - .replace(YAML_SCHEMA_PATH, '')), - ); + outputs.completed.forEach(cleanUpApiName); + outputs.failed.forEach(cleanUpApiName); + outputs.noop.forEach(cleanUpApiName); + outputs.warning?.forEach(cleanUpApiName); const { stdout: currentSha } = await exec('git', ['rev-parse', 'HEAD']); console.log( diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts index 2393e71ce8..df8ecfc723 100644 --- a/packages/repo-tools/src/lib/openapi/optic/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -132,6 +132,14 @@ const getBreakagesRow = (breakage: CiRunDetails['completed'][number]) => { )}`; }; +const addSummaryLine = (items: any[] | number | undefined, label: string) => { + const length = Array.isArray(items) ? items.length : items; + if (!length) return ''; + let text = length === 1 ? `1 API` : `${length} APIs`; + text += ` had ${label}`; + return text; +}; + export const generateCompareSummaryMarkdown = ( commit: { sha: string }, results: CiRunDetails, @@ -161,38 +169,14 @@ export const generateCompareSummaryMarkdown = ( results.completed.length - breakages.length; return `### Summary for commit (${commit.sha}) -${ - results.noop.length > 0 - ? `${ - results.noop.length === 1 ? '1 API' : `${results.noop.length} APIs` - } had no changes.` - : '' -} -${ - breakages.length > 0 - ? `${ - breakages.length === 1 ? '1 API' : `${breakages.length} APIs` - } had breaking changes.` - : '' -} -${ - successfullyCompletedCount > 0 - ? `${ - successfullyCompletedCount === 1 - ? '1 API' - : `${successfullyCompletedCount} APIs` - } had non-breaking changes.` - : '' -} -${ - results.warning && results.warning.length > 0 - ? `${ - results.warning.length === 1 - ? '1 API' - : `${results.warning.length} APIs` - } had warnings.` - : '' -} +${addSummaryLine(results.noop, 'no changes')} + +${addSummaryLine(breakages.length, 'breaking changes')} + +${addSummaryLine(successfullyCompletedCount, 'non-breaking changes')} + +${addSummaryLine(results.warning, 'warnings')} + ${ results.completed.length > 0 ? `### APIs with Changes From 61a9e8801f20671e08b9cf64dad2286611a42cbc Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Date: Mon, 1 Apr 2024 11:13:19 -0400 Subject: [PATCH 17/70] add changeset Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> --- packages/repo-tools/cli-report.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 7c7dd445bc..65c0f50163 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -168,7 +168,7 @@ Options: -h, --help Commands: - verify [options] [paths...] + verify [paths...] lint [options] [paths...] test [options] [paths...] fuzz [options] @@ -179,6 +179,14 @@ Commands: ``` Usage: backstage-repo-tools repo schema openapi fuzz [options] + check [options] + help [command] +``` + +### `backstage-repo-tools repo schema openapi check` + +``` +Usage: backstage-repo-tools repo schema openapi check [options] Options: --since @@ -211,7 +219,6 @@ Options: Usage: backstage-repo-tools repo schema openapi verify [options] [paths...] Options: - --from -h, --help ``` From 5d091a6405dc6bca517f49c2e889e46f256751fd Mon Sep 17 00:00:00 2001 From: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Date: Mon, 1 Apr 2024 19:46:05 -0400 Subject: [PATCH 18/70] revert verify changes Signed-off-by: Aramis Sennyey <159921952+aramissennyeydd@users.noreply.github.com> Signed-off-by: web-next-automation --- .../commands/repo/schema/openapi/verify.ts | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts index 2902d99ea4..23e07d1230 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/verify.ts @@ -30,14 +30,14 @@ import { } from '../../../../lib/openapi/constants'; import { getPathToOpenApiSpec } from '../../../../lib/openapi/helpers'; -const verifySpecAndGeneratedSpecMatch = async ( - openapiPath: string, - directoryPath: string, -) => { - const openapiTempDirectory = resolvePath(cliPaths.targetDir, '.openapi'); - await fs.mkdirp(openapiTempDirectory); - console.log(openapiTempDirectory); - +async function verify(directoryPath: string) { + let openapiPath = ''; + try { + openapiPath = await getPathToOpenApiSpec(directoryPath); + } catch { + // Unable to find spec at path. + return; + } const yaml = YAML.load(await fs.readFile(openapiPath, 'utf8')); await Parser.validate(cloneDeep(yaml) as any); @@ -57,18 +57,6 @@ const verifySpecAndGeneratedSpecMatch = async ( `\`${YAML_SCHEMA_PATH}\` and \`${TS_SCHEMA_PATH}\` do not match. Please run \`yarn backstage-repo-tools package schema openapi generate\` from '${path}' to regenerate \`${TS_SCHEMA_PATH}\`.`, ); } -}; - -async function verify(directoryPath: string) { - let openapiPath = ''; - try { - openapiPath = await getPathToOpenApiSpec(directoryPath); - } catch { - // Unable to find spec at path. - return; - } - - await verifySpecAndGeneratedSpecMatch(openapiPath, directoryPath); } export async function bulkCommand(paths: string[] = []): Promise { From 490d96829b582c0a707a20d81b8167d94fda5e0d Mon Sep 17 00:00:00 2001 From: web-next-automation Date: Mon, 8 Apr 2024 21:01:31 -0400 Subject: [PATCH 19/70] fix merge issue Signed-off-by: web-next-automation --- packages/repo-tools/src/lib/openapi/optic/helpers.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/repo-tools/src/lib/openapi/optic/helpers.ts b/packages/repo-tools/src/lib/openapi/optic/helpers.ts index df8ecfc723..9f5deb72e0 100644 --- a/packages/repo-tools/src/lib/openapi/optic/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/optic/helpers.ts @@ -87,9 +87,7 @@ function getOperationsText( ...[...ops.removed].map(o => `\`${o}\` (removed)`), ].join('\n') : ''; - return `${getOperationsChangedLabel(groupedDiffs, { - joiner: options.labelJoiner, - })} + return `${getOperationsChangedLabel(groupedDiffs)} ${operationsText} `; From 5826d70b544472d394b72bc2162a5fbbe8821473 Mon Sep 17 00:00:00 2001 From: web-next-automation Date: Mon, 8 Apr 2024 21:08:15 -0400 Subject: [PATCH 20/70] add cli report Signed-off-by: web-next-automation --- packages/repo-tools/cli-report.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 65c0f50163..68d679fb06 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -172,13 +172,6 @@ Commands: lint [options] [paths...] test [options] [paths...] fuzz [options] - help [command] -``` - -### `backstage-repo-tools repo schema openapi fuzz` - -``` -Usage: backstage-repo-tools repo schema openapi fuzz [options] check [options] help [command] ``` @@ -193,6 +186,16 @@ Options: -h, --help ``` +### `backstage-repo-tools repo schema openapi fuzz` + +``` +Usage: backstage-repo-tools repo schema openapi fuzz [options] + +Options: + --since + -h, --help +``` + ### `backstage-repo-tools repo schema openapi lint` ``` From fd84ca431702a0faa267b475a9a81b8c7d4b4021 Mon Sep 17 00:00:00 2001 From: web-next-automation Date: Mon, 8 Apr 2024 21:09:13 -0400 Subject: [PATCH 21/70] revert Signed-off-by: web-next-automation --- plugins/catalog-backend/src/schema/openapi.generated.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 823ac40a14..550db58f2e 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From fade5e020fe1a3e114262e6bf719753a9a58c31d Mon Sep 17 00:00:00 2001 From: CiscoRob <133238823+CiscoRob@users.noreply.github.com> Date: Sat, 6 Apr 2024 14:38:20 -0500 Subject: [PATCH 22/70] Update CatalogTable to default total count to 0 instead of undefined Depending on latency in making the request the context can load without items and then refresh moments later with the correct count. Until the entities are loaded the total will show as "All (undefined)", which is not confidence inspiring. Signed-off-by: CiscoRob <133238823+CiscoRob@users.noreply.github.com> Signed-off-by: Coderrob --- .../src/components/CatalogTable/CatalogTable.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 674271a044..ba52b7d3a0 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -88,10 +88,15 @@ export const CatalogTable = (props: CatalogTableProps) => { } = props; const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); const entityListContext = useEntityList(); - const { loading, error, entities, filters, pageInfo, totalItems } = - entityListContext; + const { + loading, + error, + entities, + filters, + pageInfo, + totalItems = 0, + } = entityListContext; const enablePagination = !!pageInfo; - const tableColumns = useMemo( () => typeof columns === 'function' ? columns(entityListContext) : columns, From 411853058ffd516f113f84daf9a467152dea4c4f Mon Sep 17 00:00:00 2001 From: Coderrob Date: Tue, 9 Apr 2024 15:46:11 -0500 Subject: [PATCH 23/70] Add changeset per contributor guide Change display to avoid displaying counts when not loaded yet Signed-off-by: Coderrob --- .changeset/swift-humans-hunt.md | 5 ++++ .../CatalogTable/CatalogTable.test.tsx | 4 ++-- .../components/CatalogTable/CatalogTable.tsx | 23 ++++++++++--------- 3 files changed, 19 insertions(+), 13 deletions(-) create mode 100644 .changeset/swift-humans-hunt.md diff --git a/.changeset/swift-humans-hunt.md b/.changeset/swift-humans-hunt.md new file mode 100644 index 0000000000..b96b3e41bc --- /dev/null +++ b/.changeset/swift-humans-hunt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Avoiding pre-loading display total count undefined for table counts diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx index 052d8f4787..35639069df 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.test.tsx @@ -343,7 +343,7 @@ describe('CatalogTable component', () => { expect(screen.getByText('Should be rendered')).toBeInTheDocument(); }); - it('should render the label column with customised title and value as specified', async () => { + it('should render the label column with customized title and value as specified', async () => { const columns = [ CatalogTable.columns.createNameColumn({ defaultKind: 'API' }), CatalogTable.columns.createLabelColumn('category', { title: 'Category' }), @@ -381,7 +381,7 @@ describe('CatalogTable component', () => { expect(labelCellValue).toBeInTheDocument(); }); - it('should render the label column with customised title and value as specified using function', async () => { + it('should render the label column with customized title and value as specified using function', async () => { const columns: CatalogTableColumnsFunc = ({ filters, entities: entities1, diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index ba52b7d3a0..08da250051 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -88,14 +88,8 @@ export const CatalogTable = (props: CatalogTableProps) => { } = props; const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); const entityListContext = useEntityList(); - const { - loading, - error, - entities, - filters, - pageInfo, - totalItems = 0, - } = entityListContext; + const { loading, error, entities, filters, pageInfo, totalItems } = + entityListContext; const enablePagination = !!pageInfo; const tableColumns = useMemo( () => @@ -175,13 +169,20 @@ export const CatalogTable = (props: CatalogTableProps) => { const currentKind = filters.kind?.value || ''; const currentType = filters.type?.value || ''; + const currentCount = Number.isSafeInteger(totalItems) + ? `(${totalItems})` + : ''; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar const titlePreamble = capitalize(filters.user?.value ?? 'all'); - const titleDisplay = [titlePreamble, currentType, pluralize(currentKind)] + const title = [ + titlePreamble, + currentType, + pluralize(currentKind), + currentCount, + ] .filter(s => s) .join(' '); - const title = `${titleDisplay} (${totalItems})`; const actions = props.actions || defaultActions; const options = { actionsColumnIndex: -1, @@ -197,7 +198,7 @@ export const CatalogTable = (props: CatalogTableProps) => { columns={tableColumns} emptyContent={emptyContent} isLoading={loading} - title={titleDisplay} + title={title} actions={actions} subtitle={subtitle} options={options} From d1c325e1c7864aa0d65259a4100a3015b852d053 Mon Sep 17 00:00:00 2001 From: Karl Haworth Date: Tue, 16 Apr 2024 07:51:02 -0400 Subject: [PATCH 24/70] - update readme to include information on wolfi package strategy Signed-off-by: Karl Haworth --- contrib/docker/minimal-harded-image/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/docker/minimal-harded-image/README.md b/contrib/docker/minimal-harded-image/README.md index accb2188ac..5737cd3be0 100644 --- a/contrib/docker/minimal-harded-image/README.md +++ b/contrib/docker/minimal-harded-image/README.md @@ -10,3 +10,5 @@ The `Dockerfile` in this directory uses a [`wolfi-base`](https://github.com/wolf - `Wolfi` OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. Some packages may be named differently. - While `Wolfi` uses `apk`, the OS is designed to support `glibc`. - Due to the stripped down nature of the base image, additional packages might be needed compared to a distribution like Debian or Ubuntu. +- Chainguard will maintain one version of each Wolfi package at a time, which will track the latest version of the upstream software in the package. Chainguard will end patch support for previous versions of packages in Wolfi. [Read more here](https://edu.chainguard.dev/chainguard/chainguard-images/faq/#what-packages-are-available-in-chainguard-images) + - It is encouraged to use a relative pin or use a third-party tool to ensure the packages are kept up to date From 3ad8f49c1decfc5f1d78838581940e5c72e14421 Mon Sep 17 00:00:00 2001 From: Karl Haworth Date: Tue, 16 Apr 2024 07:51:23 -0400 Subject: [PATCH 25/70] add info on digests Signed-off-by: Karl Haworth --- contrib/docker/minimal-harded-image/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/contrib/docker/minimal-harded-image/README.md b/contrib/docker/minimal-harded-image/README.md index 5737cd3be0..a4f19784d4 100644 --- a/contrib/docker/minimal-harded-image/README.md +++ b/contrib/docker/minimal-harded-image/README.md @@ -4,6 +4,16 @@ DockerHub images in general did not seem ideal for Backstage as the number of vu The `Dockerfile` in this directory uses a [`wolfi-base`](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners. +## Pinning Digest + +To reduce maintenance, the digest of the image has been removed from the `./Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. + +Using the digest allows tools such as Dependabot or Renovate to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. It is suggested to setup Dependabot/Renovate or a similar tool to ensure the image is kept up to date so that vulnerability fixes that have been addressed are pulled in frequently. + +### Obtaining Digest + +To obtain the latest digest, perform a `docker pull` on the image to get the latest digest in the command output or use `crane digest `. + ## Considerations - `Wolfi` only releases the `latest` tag for public consumption however digests can be pinned. From 4120800bd9e4b46c84b2e1b1436929b3f133eb07 Mon Sep 17 00:00:00 2001 From: Karl Haworth Date: Tue, 16 Apr 2024 07:51:40 -0400 Subject: [PATCH 26/70] - move path from `minimal-harded-image` -> `minimal-hardened-image` Signed-off-by: Karl Haworth --- .../Dockerfile | 0 .../README.md | 0 docs/deployment/docker.md | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename contrib/docker/{minimal-harded-image => minimal-hardened-image}/Dockerfile (100%) rename contrib/docker/{minimal-harded-image => minimal-hardened-image}/README.md (100%) diff --git a/contrib/docker/minimal-harded-image/Dockerfile b/contrib/docker/minimal-hardened-image/Dockerfile similarity index 100% rename from contrib/docker/minimal-harded-image/Dockerfile rename to contrib/docker/minimal-hardened-image/Dockerfile diff --git a/contrib/docker/minimal-harded-image/README.md b/contrib/docker/minimal-hardened-image/README.md similarity index 100% rename from contrib/docker/minimal-harded-image/README.md rename to contrib/docker/minimal-hardened-image/README.md diff --git a/docs/deployment/docker.md b/docs/deployment/docker.md index 791882224d..9ec5460a88 100644 --- a/docs/deployment/docker.md +++ b/docs/deployment/docker.md @@ -345,8 +345,8 @@ The `Dockerfile` mentioned above located in `packages/backend` is maintained by ### Minimal Hardened Image -A contributed `Dockerfile` exists within the directory of `contrib/docker/minimal-harded-image` which uses the [`wolfi-base`](https://github.com/wolfi-dev) image to reduce vulnerabilities. When this was contributed, this alternative `Dockerfile` reduced 98.2% of vulnerabilities in the built Backstage docker image when compared with the image built from `packages/backend/Dockerfile`. +A contributed `Dockerfile` exists within the directory of `contrib/docker/minimal-hardened-image` which uses the [`wolfi-base`](https://github.com/wolfi-dev) image to reduce vulnerabilities. When this was contributed, this alternative `Dockerfile` reduced 98.2% of vulnerabilities in the built Backstage docker image when compared with the image built from `packages/backend/Dockerfile`. -To reduce maintenance, the digest of the image has been removed from the `contrib/docker/minimal-harded-image/Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. Please do a docker pull on the image to get the latest digest. Using the digest allows tools such as Dependabot or Renovate to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. +To reduce maintenance, the digest of the image has been removed from the `contrib/docker/minimal-hardened-image/Dockerfile` file. A complete example with the digest would be `cgr.dev/chainguard/wolfi-base:latest@sha256:3d6dece13cdb5546cd03b20e14f9af354bc1a56ab5a7b47dca3e6c1557211fcf` and it is suggested to update the `FROM` line in the `Dockerfile` to use a digest. Please do a docker pull on the image to get the latest digest. Using the digest allows tools such as Dependabot or Renovate to know exactly which image digest is being utilized and allows for Pull Requests to be triggered when a new digest is available. It is suggested to setup Dependabot/Renovate or a similar tool to ensure the image is kept up to date so that vulnerability fixes that have been addressed are pulled in frequently. From 9d089a421916cd366e2c669983bf2d8303d1061d Mon Sep 17 00:00:00 2001 From: Karl Haworth Date: Tue, 16 Apr 2024 07:52:35 -0400 Subject: [PATCH 27/70] change item to subbullet Signed-off-by: Karl Haworth --- contrib/docker/minimal-hardened-image/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/docker/minimal-hardened-image/README.md b/contrib/docker/minimal-hardened-image/README.md index a4f19784d4..c9a41d0d19 100644 --- a/contrib/docker/minimal-hardened-image/README.md +++ b/contrib/docker/minimal-hardened-image/README.md @@ -17,7 +17,8 @@ To obtain the latest digest, perform a `docker pull` on the image to get the lat ## Considerations - `Wolfi` only releases the `latest` tag for public consumption however digests can be pinned. -- `Wolfi` OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. Some packages may be named differently. +- `Wolfi` OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. + - Some packages may be named differently. - While `Wolfi` uses `apk`, the OS is designed to support `glibc`. - Due to the stripped down nature of the base image, additional packages might be needed compared to a distribution like Debian or Ubuntu. - Chainguard will maintain one version of each Wolfi package at a time, which will track the latest version of the upstream software in the package. Chainguard will end patch support for previous versions of packages in Wolfi. [Read more here](https://edu.chainguard.dev/chainguard/chainguard-images/faq/#what-packages-are-available-in-chainguard-images) From 79d91e06af741dd2701c69c1706859ceaad9ef2e Mon Sep 17 00:00:00 2001 From: Karl Haworth Date: Tue, 16 Apr 2024 11:48:32 -0500 Subject: [PATCH 28/70] add wolfi to vale accept list Signed-off-by: Karl Haworth --- .github/vale/config/vocabularies/Backstage/accept.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index b525d7e48e..77b144dbcb 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -462,6 +462,7 @@ Weaveworks Webpack widget's winston +Wolfi www WWW XCMetrics From b55f026d43b2c8a14b4c2340470bb9cdd7903271 Mon Sep 17 00:00:00 2001 From: Karl Haworth Date: Tue, 16 Apr 2024 11:49:12 -0500 Subject: [PATCH 29/70] remove backticks from 'wolfi' Signed-off-by: Karl Haworth --- contrib/docker/minimal-hardened-image/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/docker/minimal-hardened-image/README.md b/contrib/docker/minimal-hardened-image/README.md index c9a41d0d19..62045a4ded 100644 --- a/contrib/docker/minimal-hardened-image/README.md +++ b/contrib/docker/minimal-hardened-image/README.md @@ -2,7 +2,7 @@ DockerHub images in general did not seem ideal for Backstage as the number of vulnerabilities were quite high regardless of the image used. -The `Dockerfile` in this directory uses a [`wolfi-base`](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners. +The `Dockerfile` in this directory uses a [wolfi-base](https://github.com/wolfi-dev) image from Chainguard Images. This improves the security of the application and reduces false positives in scanners. ## Pinning Digest @@ -16,10 +16,10 @@ To obtain the latest digest, perform a `docker pull` on the image to get the lat ## Considerations -- `Wolfi` only releases the `latest` tag for public consumption however digests can be pinned. -- `Wolfi` OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. +- Wolfi only releases the `latest` tag for public consumption however digests can be pinned. +- Wolfi OS uses packages from the [os repository](https://github.com/wolfi-dev/os) on GitHub. - Some packages may be named differently. -- While `Wolfi` uses `apk`, the OS is designed to support `glibc`. +- While Wolfi uses `apk`, the OS is designed to support `glibc`. - Due to the stripped down nature of the base image, additional packages might be needed compared to a distribution like Debian or Ubuntu. - Chainguard will maintain one version of each Wolfi package at a time, which will track the latest version of the upstream software in the package. Chainguard will end patch support for previous versions of packages in Wolfi. [Read more here](https://edu.chainguard.dev/chainguard/chainguard-images/faq/#what-packages-are-available-in-chainguard-images) - It is encouraged to use a relative pin or use a third-party tool to ensure the packages are kept up to date From 6f5a3a3ce5b337de29ccb54c0f887ec113d1b583 Mon Sep 17 00:00:00 2001 From: secustor Date: Wed, 17 Apr 2024 17:19:35 +0200 Subject: [PATCH 30/70] fix(plugins/catalog-backend-module-unprocessed): correctly translate owner to string in case of nullish value Signed-off-by: secustor --- .changeset/cyan-suns-shave.md | 5 +++++ .../src/UnprocessedEntitiesModule.ts | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 .changeset/cyan-suns-shave.md diff --git a/.changeset/cyan-suns-shave.md b/.changeset/cyan-suns-shave.md new file mode 100644 index 0000000000..09e51d9548 --- /dev/null +++ b/.changeset/cyan-suns-shave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-unprocessed': patch +--- + +Correctly convert owner to string in case owner has not been provided diff --git a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts index 23042ff2d8..c46848f201 100644 --- a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts +++ b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts @@ -162,7 +162,7 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'failed', - owner: String(req.query.owner), + owner: req.query.owner?.toString(), }), ); }) @@ -170,7 +170,7 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'pending', - owner: String(req.query.owner), + owner: req.query.owner?.toString(), }), ); }) From 4039d328ae08f51b550c0d6b191776b004335446 Mon Sep 17 00:00:00 2001 From: Coderrob Date: Wed, 17 Apr 2024 11:14:53 -0500 Subject: [PATCH 31/70] Adjust to make lighthouse happier Signed-off-by: Coderrob --- plugins/catalog/src/components/CatalogTable/CatalogTable.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 08da250051..b1590bdff3 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -169,9 +169,7 @@ export const CatalogTable = (props: CatalogTableProps) => { const currentKind = filters.kind?.value || ''; const currentType = filters.type?.value || ''; - const currentCount = Number.isSafeInteger(totalItems) - ? `(${totalItems})` - : ''; + const currentCount = typeof totalItems === 'number' ? `(${totalItems})` : ''; // TODO(timbonicus): remove the title from the CatalogTable once using EntitySearchBar const titlePreamble = capitalize(filters.user?.value ?? 'all'); const title = [ From c35ea7c8ecb3a6c0873ce89752cd5401f0ec695c Mon Sep 17 00:00:00 2001 From: Sebastian Poxhofer Date: Sat, 20 Apr 2024 08:47:59 +0200 Subject: [PATCH 32/70] Apply suggestions from code review Co-authored-by: Vincenzo Scamporlino Signed-off-by: Sebastian Poxhofer --- .../src/UnprocessedEntitiesModule.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts index c46848f201..6f0fa7d2e5 100644 --- a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts +++ b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts @@ -162,7 +162,7 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'failed', - owner: req.query.owner?.toString(), + owner: typeof req.query.owner === 'string' ? req.query.owner : undefined, }), ); }) @@ -170,7 +170,7 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'pending', - owner: req.query.owner?.toString(), + owner: typeof req.query.owner === 'string' ? req.query.owner : undefined }), ); }) From 821f902bfec625628ce82e38003a37c0bc9cd78f Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Sat, 20 Apr 2024 22:15:57 -0400 Subject: [PATCH 33/70] update to diff instead of check Signed-off-by: aramissennyeydd --- .changeset/flat-countries-clap.md | 2 +- packages/repo-tools/src/commands/index.ts | 14 +++++++------- .../package/schema/openapi/{check.ts => diff.ts} | 0 .../repo/schema/openapi/{check.ts => diff.ts} | 12 ++++-------- plugins/catalog-backend/package.json | 2 +- 5 files changed, 13 insertions(+), 17 deletions(-) rename packages/repo-tools/src/commands/package/schema/openapi/{check.ts => diff.ts} (100%) rename packages/repo-tools/src/commands/repo/schema/openapi/{check.ts => diff.ts} (90%) diff --git a/.changeset/flat-countries-clap.md b/.changeset/flat-countries-clap.md index ccd779a2b5..9c962b04a3 100644 --- a/.changeset/flat-countries-clap.md +++ b/.changeset/flat-countries-clap.md @@ -2,4 +2,4 @@ '@backstage/repo-tools': minor --- -Adds 2 new commands `repo schema openapi check` and `package schema openapi check`. `repo schema openapi check` is intended to power a new breaking changes check on pull requests and the package level command allows plugin developers to quickly see new API breaking changes.They're intended to be used in complement with the existing `repo schema openapi verify` command to validate your OpenAPI spec against a variety of things. +Adds 2 new commands `repo schema openapi diff` and `package schema openapi diff`. `repo schema openapi diff` is intended to power a new breaking changes check on pull requests and the package level command allows plugin developers to quickly see new API breaking changes. They're intended to be used in complement with the existing `repo schema openapi verify` command to validate your OpenAPI spec against a variety of things. diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index 3fa16aa512..83266ea9f9 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -80,12 +80,12 @@ function registerPackageCommand(program: Command) { ); openApiCommand - .command('check') + .command('diff') .option('--ignore', 'Ignore linting failures and only log the results.') .option('--json', 'Output the results as JSON') - .option('--since ', 'Check the API against a specific ref') + .option('--since ', 'Diff the API against a specific ref') .action( - lazy(() => import('./package/schema/openapi/check').then(m => m.command)), + lazy(() => import('./package/schema/openapi/diff').then(m => m.command)), ); } @@ -144,17 +144,17 @@ function registerRepoCommand(program: Command) { ); openApiCommand - .command('check') + .command('diff') .description( - 'Check the repository against a specific ref, will run all package `check:api` scripts.', + 'Diff the repository against a specific ref, will run all package `diff` scripts.', ) .option( '--since ', - 'Check the API against a specific ref', + 'Diff the API against a specific ref', 'origin/master', ) .action( - lazy(() => import('./repo/schema/openapi/check').then(m => m.command)), + lazy(() => import('./repo/schema/openapi/diff').then(m => m.command)), ); } diff --git a/packages/repo-tools/src/commands/package/schema/openapi/check.ts b/packages/repo-tools/src/commands/package/schema/openapi/diff.ts similarity index 100% rename from packages/repo-tools/src/commands/package/schema/openapi/check.ts rename to packages/repo-tools/src/commands/package/schema/openapi/diff.ts diff --git a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts b/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts similarity index 90% rename from packages/repo-tools/src/commands/repo/schema/openapi/check.ts rename to packages/repo-tools/src/commands/repo/schema/openapi/diff.ts index 95bd4dffed..02f3db0c43 100644 --- a/packages/repo-tools/src/commands/repo/schema/openapi/check.ts +++ b/packages/repo-tools/src/commands/repo/schema/openapi/diff.ts @@ -54,9 +54,7 @@ export async function command(opts: OptionValues) { ); } - const checkablePackages = packages.filter( - e => e.packageJson.scripts?.['check:api'], - ); + const checkablePackages = packages.filter(e => e.packageJson.scripts?.diff); try { const outputs = { @@ -70,7 +68,7 @@ export async function command(opts: OptionValues) { const sinceCommands = since ? ['--since', since] : []; const { stdout } = await exec( 'yarn', - ['check:api', '--ignore', '--json', ...sinceCommands], + ['diff', '--ignore', '--json', ...sinceCommands], { cwd: pkg.dir, }, @@ -81,12 +79,10 @@ export async function command(opts: OptionValues) { outputs.noop.push(...(result.noop ?? [])); } - for (const pkg of packages.filter( - e => !e.packageJson.scripts?.['check:api'], - )) { + for (const pkg of packages.filter(e => !e.packageJson.scripts?.diff)) { outputs.warning?.push({ apiName: `${pkg.dir}/`, - warning: 'No check:api script found in package.json', + warning: 'No diff script found in package.json', }); } diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 058b9318d7..04a7778926 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -42,8 +42,8 @@ ], "scripts": { "build": "backstage-cli package build", - "check:api": "backstage-repo-tools package schema openapi check", "clean": "backstage-cli package clean", + "diff": "backstage-repo-tools package schema openapi diff", "fuzz": "backstage-repo-tools package schema openapi fuzz --exclude-checks response_schema_conformance", "generate": "backstage-repo-tools package schema openapi generate --server --client-package packages/catalog-client", "lint": "backstage-cli package lint", From f4856e9f9b1d950565a32e431a4c835a82354e91 Mon Sep 17 00:00:00 2001 From: aramissennyeydd Date: Mon, 22 Apr 2024 09:26:20 -0400 Subject: [PATCH 34/70] add api report Signed-off-by: aramissennyeydd --- packages/repo-tools/cli-report.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 68d679fb06..a06f426baf 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -172,14 +172,14 @@ Commands: lint [options] [paths...] test [options] [paths...] fuzz [options] - check [options] + diff [options] help [command] ``` -### `backstage-repo-tools repo schema openapi check` +### `backstage-repo-tools repo schema openapi diff` ``` -Usage: backstage-repo-tools repo schema openapi check [options] +Usage: backstage-repo-tools repo schema openapi diff [options] Options: --since From 73a963492e5453a118ede38cb790ccf3a8be438e Mon Sep 17 00:00:00 2001 From: Himesh Ladva Date: Thu, 25 Apr 2024 09:52:04 +0100 Subject: [PATCH 35/70] chore(docs): update openapi generate-client command Signed-off-by: Himesh Ladva --- docs/openapi/generate-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md index 640036960e..b8acc331e5 100644 --- a/docs/openapi/generate-client.md +++ b/docs/openapi/generate-client.md @@ -20,7 +20,7 @@ info: ### Generating your client -1. Run `yarn backstage-repo-tools schema openapi generate client --output-package `. This will create a new folder in `/src/generated` to house the generated content. +1. Run `yarn backstage-repo-tools package schema openapi generate client --client-package `. This will create a new folder in `/src/generated` to house the generated content. 2. You should use the generated files as follows, - `apis/DefaultApi.client.ts` - this is the client that you should use. It has types for all of the various operations on your API. From 8479a0be3fb631116e6cb3a8ee5857fc085fc89f Mon Sep 17 00:00:00 2001 From: zcmander Date: Thu, 25 Apr 2024 14:11:18 +0300 Subject: [PATCH 36/70] fix: stitching queue gauge to not include entities from the future Signed-off-by: zcmander --- .changeset/sweet-zoos-clap.md | 5 +++++ plugins/catalog-backend/src/stitching/progressTracker.ts | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .changeset/sweet-zoos-clap.md diff --git a/.changeset/sweet-zoos-clap.md b/.changeset/sweet-zoos-clap.md new file mode 100644 index 0000000000..8c76f4abd7 --- /dev/null +++ b/.changeset/sweet-zoos-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Fixed bug in stitching queue gauge that included entities that are scheduled in the future. diff --git a/plugins/catalog-backend/src/stitching/progressTracker.ts b/plugins/catalog-backend/src/stitching/progressTracker.ts index 9e00a1a01b..8c305b4978 100644 --- a/plugins/catalog-backend/src/stitching/progressTracker.ts +++ b/plugins/catalog-backend/src/stitching/progressTracker.ts @@ -54,7 +54,8 @@ export function progressTracker(knex: Knex, logger: LoggerService) { stitchingQueueCount.addCallback(async result => { const total = await knex('refresh_state') .count({ count: '*' }) - .whereNotNull('next_stitch_at'); + .whereNotNull('next_stitch_at') + .where('next_stitch_at', '<=', knex.fn.now()); result.observe(Number(total[0].count)); }); From 6d89bf4f720985c896778a4f4d4617c99529d071 Mon Sep 17 00:00:00 2001 From: secustor Date: Thu, 25 Apr 2024 19:59:51 +0200 Subject: [PATCH 37/70] prettier fix Signed-off-by: secustor --- .../src/UnprocessedEntitiesModule.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts index 6f0fa7d2e5..34aedd61af 100644 --- a/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts +++ b/plugins/catalog-backend-module-unprocessed/src/UnprocessedEntitiesModule.ts @@ -162,7 +162,8 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'failed', - owner: typeof req.query.owner === 'string' ? req.query.owner : undefined, + owner: + typeof req.query.owner === 'string' ? req.query.owner : undefined, }), ); }) @@ -170,7 +171,8 @@ export class UnprocessedEntitiesModule { return res.json( await this.unprocessed({ reason: 'pending', - owner: typeof req.query.owner === 'string' ? req.query.owner : undefined + owner: + typeof req.query.owner === 'string' ? req.query.owner : undefined, }), ); }) From 3ef8cbc9cc4cf99782aedbfaabc6fe3344ddf4c7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Apr 2024 00:20:29 +0000 Subject: [PATCH 38/70] chore(deps): update github/codeql-action action to v3.25.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/verify_codeql.yml | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 91f1b02fee..67fdff4020 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -66,6 +66,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 70fb8a44d9..e2507996b1 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/upload-sarif@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 with: sarif_file: snyk.sarif diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index e82577763e..920c9bab17 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/init@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/autobuild@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1 + uses: github/codeql-action/analyze@d39d31e687223d841ef683f52467bd88e9b21c14 # v3.25.3 From ba765801da0547f1d991d2f0569e76a7d0eef771 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 26 Apr 2024 22:52:06 +0530 Subject: [PATCH 39/70] microsite to v3 Signed-off-by: npiyush97 --- docs/publishing.md | 2 +- microsite/docusaurus.config.js | 349 --------------------------------- microsite/docusaurus.config.ts | 349 +++++++++++++++++++++++++++++++++ microsite/package.json | 6 +- microsite/tsconfig.json | 2 +- microsite/yarn.lock | 33 ++-- 6 files changed, 368 insertions(+), 373 deletions(-) delete mode 100644 microsite/docusaurus.config.js create mode 100644 microsite/docusaurus.config.ts diff --git a/docs/publishing.md b/docs/publishing.md index 29853955f1..d7a4f65008 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -52,7 +52,7 @@ Additional steps for the main line release - Create Release Notes PR - Add the release note file as [`/docs/releases/vx.y.0.md`](./releases) - Add an entry to [`/microsite/sidebar.json`](https://github.com/backstage/backstage/blob/master/microsite/sidebars.json) for the release note - - Update the navigation bar item in [`/microsite/docusaurus.config.js`](https://github.com/backstage/backstage/blob/master/microsite/docusaurus.config.js) to point to the new release note + - Update the navigation bar item in [`/microsite/docusaurus.config.ts`](https://github.com/backstage/backstage/blob/master/microsite/docusaurus.config.ts) to point to the new release note - Finally copy the content, without the metadata header, into the description of the [`Version Packages` Pull Request](https://github.com/backstage/backstage/pulls?q=is%3Aopen+is%3Apr+in%3Atitle+%22Version+Packages) Once the release has been published edit the newly created release in the [GitHub repository](https://github.com/backstage/backstage/releases) and replace the text content with the release notes. diff --git a/microsite/docusaurus.config.js b/microsite/docusaurus.config.js deleted file mode 100644 index 1866deda5c..0000000000 --- a/microsite/docusaurus.config.js +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * 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. - */ - -// @ts-check - -/** @type{import('prism-react-renderer').PrismTheme} **/ -// @ts-ignore -const prismTheme = require('prism-react-renderer/themes/vsDark'); -prismTheme.plain.backgroundColor = '#232323'; - -/** @type {import('@docusaurus/types').Config} */ -module.exports = { - title: 'Backstage Software Catalog and Developer Platform', - tagline: 'An open source framework for building developer portals', - url: 'https://backstage.io', - baseUrl: '/', - organizationName: 'Spotify', - projectName: 'backstage', - scripts: [ - 'https://buttons.github.io/buttons.js', - 'https://unpkg.com/medium-zoom@1.0.6/dist/medium-zoom.min.js', - '/js/medium-zoom.js', - '/js/dismissable-banner.js', - '/js/scroll-nav-to-view-in-docs.js', - ], - stylesheets: [ - 'https://fonts.googleapis.com/css?family=IBM+Plex+Mono:500,700&display=swap', - ], - favicon: 'img/favicon.ico', - customFields: { - fossWebsite: 'https://spotify.github.io/', - repoUrl: 'https://github.com/backstage/backstage', - }, - onBrokenLinks: 'log', - onBrokenMarkdownLinks: 'log', - presets: [ - [ - '@docusaurus/preset-classic', - /** @type {import('@docusaurus/preset-classic').Options} */ - { - docs: { - editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', - path: '../docs', - sidebarPath: 'sidebars.json', - }, - blog: { - path: 'blog', - }, - theme: { - customCss: 'src/theme/customTheme.scss', - }, - gtag: { - trackingID: 'G-KSEVGGNCJW', - }, - }, - ], - ], - markdown: { - preprocessor({ fileContent }) { - // Replace all HTML comments with empty strings as these are not supported by MDXv2. - return fileContent.replace(//gs, ''); - }, - format: 'detect', - }, - webpack: { - jsLoader: isServer => ({ - loader: require.resolve('swc-loader'), - options: { - jsc: { - parser: { - syntax: 'typescript', - tsx: true, - }, - target: 'es2017', - }, - module: { - type: isServer ? 'commonjs' : 'es6', - }, - }, - }), - }, - plugins: [ - 'docusaurus-plugin-sass', - () => ({ - name: 'yaml-loader', - configureWebpack() { - return { - module: { - rules: [ - { - test: /\.ya?ml$/, - use: 'yaml-loader', - }, - ], - }, - }; - }, - }), - [ - '@docusaurus/plugin-client-redirects', - { - redirects: [ - { - from: '/docs', - to: '/docs/overview/what-is-backstage', - }, - { - from: '/docs/features/software-catalog/software-catalog-overview', - to: '/docs/features/software-catalog/', - }, - { - from: '/docs/features/software-templates/software-templates-index', - to: '/docs/features/software-templates/', - }, - { - from: '/docs/features/techdocs/techdocs-overview', - to: '/docs/features/techdocs/', - }, - { - from: '/docs/features/kubernetes/overview', - to: '/docs/features/kubernetes/', - }, - { - from: '/docs/features/search/search-overview', - to: '/docs/features/search/', - }, - { - from: '/docs/getting-started/running-backstage-locally', - to: '/docs/getting-started/', - }, - { - from: '/docs/features/software-templates/testing-scaffolder-alpha', - to: '/docs/features/software-templates/migrating-to-rjsf-v5', - }, - { - from: '/docs/auth/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/overview/glossary', - to: '/docs/references/glossary', - }, - { - from: '/docs/getting-started/create-an-app', - to: '/docs/getting-started/', - }, - { - from: '/docs/getting-started/configuration', - to: '/docs/getting-started/#next-steps', - }, - ], - }, - ], - [ - 'docusaurus-pushfeedback', - { - project: 'q8w1i6cair', - hideIcon: true, - customFont: true, - buttonStyle: 'dark', - }, - ], - ], - themeConfig: - /** @type {import('@docusaurus/preset-classic').ThemeConfig} */ - { - colorMode: { - defaultMode: 'dark', - disableSwitch: true, - }, - navbar: { - logo: { - alt: 'Backstage Software Catalog and Developer Platform', - src: 'img/logo.svg', - }, - items: [ - { - href: 'https://github.com/backstage/backstage', - label: 'GitHub', - position: 'left', - }, - { - to: 'docs/overview/what-is-backstage', - label: 'Docs', - position: 'left', - }, - { - to: '/plugins', - label: 'Plugins', - position: 'left', - }, - { - to: '/blog', - label: 'Blog', - position: 'left', - }, - { - to: 'docs/releases/v1.26.0', - label: 'Releases', - position: 'left', - }, - { - to: '/demos', - label: 'Demos', - position: 'left', - }, - { - to: '/community', - label: 'Community', - position: 'left', - }, - ], - }, - image: 'img/sharing-opengraph.png', - footer: { - links: [ - { - items: [ - { - html: ` - - - `, - }, - ], - }, - { - title: 'Docs', - items: [ - { - label: 'What is Backstage?', - to: 'docs/overview/what-is-backstage', - }, - { - label: 'Getting started', - to: 'docs/getting-started/', - }, - { - label: 'Software Catalog', - to: 'docs/features/software-catalog/', - }, - { - label: 'Create a Plugin', - to: 'docs/plugins/create-a-plugin', - }, - { - label: 'Designing for Backstage', - to: 'docs/dls/design', - }, - ], - }, - { - title: 'Community', - items: [ - { - label: 'Support chatroom', - to: 'https://discord.gg/backstage-687207715902193673', - }, - { - label: 'Contributing', - to: 'https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md', - }, - { - label: 'Adopting', - to: 'https://backstage.spotify.com', - }, - { - label: 'Subscribe to our newsletter', - to: 'https://info.backstage.spotify.com/newsletter_subscribe', - }, - { - label: 'CNCF Incubation', - to: 'https://www.cncf.io/projects/', - }, - ], - }, - { - title: 'More', - items: [ - { - label: 'Open Source @ Spotify', - to: 'https://spotify.github.io/', - }, - { - label: 'Spotify Engineering Blog', - to: 'https://engineering.atspotify.com/', - }, - { - label: 'Spotify for Developers', - to: 'https://developer.spotify.com/', - }, - { - label: 'GitHub', - to: 'https://github.com/backstage/', - }, - ], - }, - ], - copyright: - '

Made with ❤️ at Spotify

', - }, - algolia: { - apiKey: '1f0ba86672ccfc3576faa94583e5b318', - indexName: 'crawler_Backstage Docusaurus 2', - appId: 'JCMFNHCHI8', - searchParameters: {}, - }, - prism: { - theme: prismTheme, - // Supported languages: https://prismjs.com/#supported-languages - // Default languages: https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/generate-prism-languages/index.ts#L9-L23 - additionalLanguages: ['docker', 'bash'], - magicComments: [ - // Extend the default highlight class name - { - className: 'code-block-highlight-line', - line: 'highlight-next-line', - block: { start: 'highlight-start', end: 'highlight-end' }, - }, - { - className: 'code-block-add-line', - line: 'highlight-add-next-line', - block: { start: 'highlight-add-start', end: 'highlight-add-end' }, - }, - { - className: 'code-block-remove-line', - line: 'highlight-remove-next-line', - block: { - start: 'highlight-remove-start', - end: 'highlight-remove-end', - }, - }, - ], - }, - }, -}; diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts new file mode 100644 index 0000000000..256784ddcc --- /dev/null +++ b/microsite/docusaurus.config.ts @@ -0,0 +1,349 @@ +/* + * Copyright 2022 The Backstage Authors + * + * 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. + */ + +// @ts-check + +/** @type{import('prism-react-renderer').PrismTheme} **/ +// @ts-ignore +import { themes } from 'prism-react-renderer'; +import type * as Preset from '@docusaurus/preset-classic'; +import { Config } from '@docusaurus/types'; +const backstageTheme = themes.vsDark; +backstageTheme.plain.backgroundColor = '#232323'; + +const config: Config = { + title: 'Backstage Software Catalog and Developer Platform', + tagline: 'An open source framework for building developer portals', + url: 'https://backstage.io', + baseUrl: '/', + organizationName: 'Spotify', + projectName: 'backstage', + scripts: [ + 'https://buttons.github.io/buttons.js', + 'https://unpkg.com/medium-zoom@1.0.6/dist/medium-zoom.min.js', + '/js/medium-zoom.js', + '/js/dismissable-banner.js', + '/js/scroll-nav-to-view-in-docs.js', + ], + stylesheets: [ + 'https://fonts.googleapis.com/css?family=IBM+Plex+Mono:500,700&display=swap', + ], + favicon: 'img/favicon.ico', + customFields: { + fossWebsite: 'https://spotify.github.io/', + repoUrl: 'https://github.com/backstage/backstage', + }, + onBrokenLinks: 'log', + onBrokenMarkdownLinks: 'log', + presets: [ + [ + '@docusaurus/preset-classic', + /** @type {import('@docusaurus/preset-classic').Options} */ + { + docs: { + editUrl: 'https://github.com/backstage/backstage/edit/master/docs/', + path: '../docs', + sidebarPath: 'sidebars.json', + }, + blog: { + path: 'blog', + }, + theme: { + customCss: 'src/theme/customTheme.scss', + }, + gtag: { + trackingID: 'G-KSEVGGNCJW', + }, + }, + ], + ], + markdown: { + preprocessor({ fileContent }) { + // Replace all HTML comments with empty strings as these are not supported by MDXv2. + return fileContent.replace(//gs, ''); + }, + format: 'detect', + }, + webpack: { + jsLoader: isServer => ({ + loader: require.resolve('swc-loader'), + options: { + jsc: { + parser: { + syntax: 'typescript', + tsx: true, + }, + target: 'es2017', + }, + module: { + type: isServer ? 'commonjs' : 'es6', + }, + }, + }), + }, + plugins: [ + 'docusaurus-plugin-sass', + () => ({ + name: 'yaml-loader', + configureWebpack() { + return { + module: { + rules: [ + { + test: /\.ya?ml$/, + use: 'yaml-loader', + }, + ], + }, + }; + }, + }), + [ + '@docusaurus/plugin-client-redirects', + { + redirects: [ + { + from: '/docs', + to: '/docs/overview/what-is-backstage', + }, + { + from: '/docs/features/software-catalog/software-catalog-overview', + to: '/docs/features/software-catalog/', + }, + { + from: '/docs/features/software-templates/software-templates-index', + to: '/docs/features/software-templates/', + }, + { + from: '/docs/features/techdocs/techdocs-overview', + to: '/docs/features/techdocs/', + }, + { + from: '/docs/features/kubernetes/overview', + to: '/docs/features/kubernetes/', + }, + { + from: '/docs/features/search/search-overview', + to: '/docs/features/search/', + }, + { + from: '/docs/getting-started/running-backstage-locally', + to: '/docs/getting-started/', + }, + { + from: '/docs/features/software-templates/testing-scaffolder-alpha', + to: '/docs/features/software-templates/migrating-to-rjsf-v5', + }, + { + from: '/docs/auth/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/overview/glossary', + to: '/docs/references/glossary', + }, + { + from: '/docs/getting-started/create-an-app', + to: '/docs/getting-started/', + }, + { + from: '/docs/getting-started/configuration', + to: '/docs/getting-started/#next-steps', + }, + ], + }, + ], + [ + 'docusaurus-pushfeedback', + { + project: 'q8w1i6cair', + hideIcon: true, + customFont: true, + buttonStyle: 'dark', + }, + ], + ], + themeConfig: { + colorMode: { + defaultMode: 'dark', + disableSwitch: true, + }, + navbar: { + logo: { + alt: 'Backstage Software Catalog and Developer Platform', + src: 'img/logo.svg', + }, + items: [ + { + href: 'https://github.com/backstage/backstage', + label: 'GitHub', + position: 'left', + }, + { + to: 'docs/overview/what-is-backstage', + label: 'Docs', + position: 'left', + }, + { + to: '/plugins', + label: 'Plugins', + position: 'left', + }, + { + to: '/blog', + label: 'Blog', + position: 'left', + }, + { + to: 'docs/releases/v1.26.0', + label: 'Releases', + position: 'left', + }, + { + to: '/demos', + label: 'Demos', + position: 'left', + }, + { + to: '/community', + label: 'Community', + position: 'left', + }, + ], + }, + image: 'img/sharing-opengraph.png', + footer: { + links: [ + { + items: [ + { + html: ` + + + `, + }, + ], + }, + { + title: 'Docs', + items: [ + { + label: 'What is Backstage?', + to: 'docs/overview/what-is-backstage', + }, + { + label: 'Getting started', + to: 'docs/getting-started/', + }, + { + label: 'Software Catalog', + to: 'docs/features/software-catalog/', + }, + { + label: 'Create a Plugin', + to: 'docs/plugins/create-a-plugin', + }, + { + label: 'Designing for Backstage', + to: 'docs/dls/design', + }, + ], + }, + { + title: 'Community', + items: [ + { + label: 'Support chatroom', + to: 'https://discord.gg/backstage-687207715902193673', + }, + { + label: 'Contributing', + to: 'https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md', + }, + { + label: 'Adopting', + to: 'https://backstage.spotify.com', + }, + { + label: 'Subscribe to our newsletter', + to: 'https://info.backstage.spotify.com/newsletter_subscribe', + }, + { + label: 'CNCF Incubation', + to: 'https://www.cncf.io/projects/', + }, + ], + }, + { + title: 'More', + items: [ + { + label: 'Open Source @ Spotify', + to: 'https://spotify.github.io/', + }, + { + label: 'Spotify Engineering Blog', + to: 'https://engineering.atspotify.com/', + }, + { + label: 'Spotify for Developers', + to: 'https://developer.spotify.com/', + }, + { + label: 'GitHub', + to: 'https://github.com/backstage/', + }, + ], + }, + ], + copyright: `

Made with ❤️ at Spotify

`, + }, + algolia: { + apiKey: '1f0ba86672ccfc3576faa94583e5b318', + indexName: 'crawler_Backstage Docusaurus 2', + appId: 'JCMFNHCHI8', + searchParameters: {}, + }, + prism: { + theme: backstageTheme, + // Supported languages: https://prismjs.com/#supported-languages + // Default languages: https://github.com/FormidableLabs/prism-react-renderer/blob/master/packages/generate-prism-languages/index.ts#L9-L23 + additionalLanguages: ['docker', 'bash'], + magicComments: [ + // Extend the default highlight class name + { + className: 'code-block-highlight-line', + line: 'highlight-next-line', + block: { start: 'highlight-start', end: 'highlight-end' }, + }, + { + className: 'code-block-add-line', + line: 'highlight-add-next-line', + block: { start: 'highlight-add-start', end: 'highlight-add-end' }, + }, + { + className: 'code-block-remove-line', + line: 'highlight-remove-next-line', + block: { + start: 'highlight-remove-start', + end: 'highlight-remove-end', + }, + }, + ], + }, + } satisfies Preset.ThemeConfig, +}; +export default config; diff --git a/microsite/package.json b/microsite/package.json index 1be5272286..edc4c08612 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -22,12 +22,14 @@ "@docusaurus/core": "^3.1.1", "@docusaurus/plugin-client-redirects": "^3.1.1", "@docusaurus/preset-classic": "^3.1.1", + "@docusaurus/types": "^3.1.1", + "@mdx-js/react": "^3.0.0", "@swc/core": "^1.3.46", "clsx": "^2.0.0", "docusaurus-plugin-sass": "^0.2.3", "docusaurus-pushfeedback": "^1.0.0", "luxon": "^3.0.0", - "prism-react-renderer": "^1.3.5", + "prism-react-renderer": "^2.1.0", "react": "^18.0.0", "react-dom": "^18.0.0", "sass": "^1.57.1", @@ -35,8 +37,8 @@ }, "devDependencies": { "@docusaurus/module-type-aliases": "^3.1.1", + "@docusaurus/tsconfig": "^3.1.1", "@spotify/prettier-config": "^15.0.0", - "@tsconfig/docusaurus": "^2.0.0", "@types/luxon": "^3.0.0", "@types/webpack-env": "^1.18.0", "js-yaml": "^4.1.0", diff --git a/microsite/tsconfig.json b/microsite/tsconfig.json index ae8fe2298a..71bc403643 100644 --- a/microsite/tsconfig.json +++ b/microsite/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@tsconfig/docusaurus/tsconfig.json", + "extends": "@docusaurus/tsconfig", "compilerOptions": { "baseUrl": ".", "types": [ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 259bbca7b0..3ca3e2837a 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2160,7 +2160,14 @@ __metadata: languageName: node linkType: hard -"@docusaurus/types@npm:3.2.1": +"@docusaurus/tsconfig@npm:^3.1.1": + version: 3.2.1 + resolution: "@docusaurus/tsconfig@npm:3.2.1" + checksum: ea3c28b79b0de069c50f7b3a67d3ff682b6ded2ef02d2c7a4c2eaeddc8fcf79c9d9f5e60fbd2966cf3d247fbb8f63897b80a61fdd8b485c745a12eb684ae241a + languageName: node + linkType: hard + +"@docusaurus/types@npm:3.2.1, @docusaurus/types@npm:^3.1.1": version: 3.2.1 resolution: "@docusaurus/types@npm:3.2.1" dependencies: @@ -2842,13 +2849,6 @@ __metadata: languageName: node linkType: hard -"@tsconfig/docusaurus@npm:^2.0.0": - version: 2.0.3 - resolution: "@tsconfig/docusaurus@npm:2.0.3" - checksum: d8245a64bf131daa0c287649cb9c37ad8fecb2aedea18e15047e0df6463916e5012af3755bd73ecf90999ace1f14a3748a8b3e041d5158e2a85c22a539c649c9 - languageName: node - linkType: hard - "@types/acorn@npm:^4.0.0": version: 4.0.6 resolution: "@types/acorn@npm:4.0.6" @@ -3847,9 +3847,11 @@ __metadata: "@docusaurus/module-type-aliases": ^3.1.1 "@docusaurus/plugin-client-redirects": ^3.1.1 "@docusaurus/preset-classic": ^3.1.1 + "@docusaurus/tsconfig": ^3.1.1 + "@docusaurus/types": ^3.1.1 + "@mdx-js/react": ^3.0.0 "@spotify/prettier-config": ^15.0.0 "@swc/core": ^1.3.46 - "@tsconfig/docusaurus": ^2.0.0 "@types/luxon": ^3.0.0 "@types/webpack-env": ^1.18.0 clsx: ^2.0.0 @@ -3858,7 +3860,7 @@ __metadata: js-yaml: ^4.1.0 luxon: ^3.0.0 prettier: ^2.6.2 - prism-react-renderer: ^1.3.5 + prism-react-renderer: ^2.1.0 react: ^18.0.0 react-dom: ^18.0.0 sass: ^1.57.1 @@ -9583,16 +9585,7 @@ __metadata: languageName: node linkType: hard -"prism-react-renderer@npm:^1.3.5": - version: 1.3.5 - resolution: "prism-react-renderer@npm:1.3.5" - peerDependencies: - react: ">=0.14.9" - checksum: c18806dcbc4c0b4fd6fd15bd06b4f7c0a6da98d93af235c3e970854994eb9b59e23315abb6cfc29e69da26d36709a47e25da85ab27fed81b6812f0a52caf6dfa - languageName: node - linkType: hard - -"prism-react-renderer@npm:^2.3.0": +"prism-react-renderer@npm:^2.1.0, prism-react-renderer@npm:^2.3.0": version: 2.3.1 resolution: "prism-react-renderer@npm:2.3.1" dependencies: From cfacb71538028155f94961b303e4f60d32b1d2b7 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Fri, 26 Apr 2024 23:01:58 +0530 Subject: [PATCH 40/70] recommeded codeql fix Signed-off-by: npiyush97 --- microsite/docusaurus.config.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index 256784ddcc..e2ae4ab338 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -73,7 +73,15 @@ const config: Config = { markdown: { preprocessor({ fileContent }) { // Replace all HTML comments with empty strings as these are not supported by MDXv2. - return fileContent.replace(//gs, ''); + function removeHtmlComments(input) { + let previous; + do { + previous = input; + input = input.replace(//gs, ''); } while (input !== previous); return input; } From 12a5feff7a9fd4c6e3c844f25da850aff8de77d6 Mon Sep 17 00:00:00 2001 From: Chap Ambrose Date: Tue, 30 Apr 2024 16:52:20 -0500 Subject: [PATCH 57/70] seperate ensureSchemaExists config Signed-off-by: Chap Ambrose --- .../src/database/connectors/postgres.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index 1ddc096e05..1aa4d2afa9 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -321,7 +321,7 @@ export class PgConnector implements Connector { let schemaOverrides; if (this.getPluginDivisionModeConfig() === 'schema') { schemaOverrides = this.getSchemaOverrides(pluginId); - if (this.getEnsureExistsConfig(pluginId)) { + if (this.getEnsureSchemaExistsConfig(pluginId)) { try { await pgConnector.ensureSchemaExists!(pluginConfig, pluginId); } catch (error) { @@ -437,6 +437,15 @@ export class PgConnector implements Connector { ); } + private getEnsureSchemaExistsConfig(pluginId: string): boolean { + const baseConfig = + this.config.getOptionalBoolean('ensureSchemaExists') ?? true; + return ( + this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? + baseConfig + ); + } + private getPluginDivisionModeConfig(): string { return this.config.getOptionalString('pluginDivisionMode') ?? 'database'; } From 86ae51bb4ad512d9c569e192fcf5ccaa0cf8b698 Mon Sep 17 00:00:00 2001 From: Chap Ambrose Date: Wed, 1 May 2024 08:38:17 -0500 Subject: [PATCH 58/70] fixed getEnsureSchemaExistsConfig Signed-off-by: Chap Ambrose --- packages/backend-common/src/database/connectors/postgres.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index 1aa4d2afa9..126c8cb89c 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -441,8 +441,9 @@ export class PgConnector implements Connector { const baseConfig = this.config.getOptionalBoolean('ensureSchemaExists') ?? true; return ( - this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? - baseConfig + this.config.getOptionalBoolean( + `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`, + ) ?? baseConfig ); } From ccc8851bd01a9f05bc4ce8e71f11a417c90cfdb4 Mon Sep 17 00:00:00 2001 From: Chap Ambrose Date: Wed, 1 May 2024 09:05:30 -0500 Subject: [PATCH 59/70] add changeset Signed-off-by: Chap Ambrose --- .changeset/heavy-trainers-fly.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/heavy-trainers-fly.md diff --git a/.changeset/heavy-trainers-fly.md b/.changeset/heavy-trainers-fly.md new file mode 100644 index 0000000000..db09f63496 --- /dev/null +++ b/.changeset/heavy-trainers-fly.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +add ensureSchemaExists backend database config From 0b8b8e80c8a6bfe74420d40c0f91fa6dcdcfeefe Mon Sep 17 00:00:00 2001 From: Chap Ambrose Date: Wed, 1 May 2024 10:07:08 -0500 Subject: [PATCH 60/70] set ensureSchemaExists to false to match current behavior Signed-off-by: Chap Ambrose --- .changeset/heavy-trainers-fly.md | 2 +- .../backend-common/src/database/connectors/postgres.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.changeset/heavy-trainers-fly.md b/.changeset/heavy-trainers-fly.md index db09f63496..48560b7ddd 100644 --- a/.changeset/heavy-trainers-fly.md +++ b/.changeset/heavy-trainers-fly.md @@ -2,4 +2,4 @@ '@backstage/backend-common': patch --- -add ensureSchemaExists backend database config +Added config prop `ensureSchemaExists` to support postgres instances where user can create schemas but not databases. diff --git a/packages/backend-common/src/database/connectors/postgres.ts b/packages/backend-common/src/database/connectors/postgres.ts index 126c8cb89c..b81bfd5b50 100644 --- a/packages/backend-common/src/database/connectors/postgres.ts +++ b/packages/backend-common/src/database/connectors/postgres.ts @@ -321,7 +321,10 @@ export class PgConnector implements Connector { let schemaOverrides; if (this.getPluginDivisionModeConfig() === 'schema') { schemaOverrides = this.getSchemaOverrides(pluginId); - if (this.getEnsureSchemaExistsConfig(pluginId)) { + if ( + this.getEnsureSchemaExistsConfig(pluginId) || + this.getEnsureExistsConfig(pluginId) + ) { try { await pgConnector.ensureSchemaExists!(pluginConfig, pluginId); } catch (error) { @@ -439,7 +442,7 @@ export class PgConnector implements Connector { private getEnsureSchemaExistsConfig(pluginId: string): boolean { const baseConfig = - this.config.getOptionalBoolean('ensureSchemaExists') ?? true; + this.config.getOptionalBoolean('ensureSchemaExists') ?? false; return ( this.config.getOptionalBoolean( `${pluginPath(pluginId)}.getEnsureSchemaExistsConfig`, From d541ff686f828c9b7a18e447e2dc81359f5918fe Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Thu, 2 May 2024 08:11:35 +0300 Subject: [PATCH 61/70] fix: email processor esm issue with p-throttle + config read Signed-off-by: Heikki Hellgren --- .changeset/cyan-eagles-hammer.md | 6 ++++++ .github/renovate.json5 | 4 ++++ .../notifications-backend-module-email/package.json | 2 +- .../processor/NotificationsEmailProcessor.test.ts | 12 ++++++------ .../src/processor/NotificationsEmailProcessor.ts | 2 +- yarn.lock | 10 +++++----- 6 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 .changeset/cyan-eagles-hammer.md diff --git a/.changeset/cyan-eagles-hammer.md b/.changeset/cyan-eagles-hammer.md new file mode 100644 index 0000000000..dfe6f0bc62 --- /dev/null +++ b/.changeset/cyan-eagles-hammer.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +'@backstage/plugin-notifications-backend': patch +--- + +Fixed email processor `esm` issue and config reading diff --git a/.github/renovate.json5 b/.github/renovate.json5 index a9cb54912a..1c8d611c49 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -62,6 +62,10 @@ matchPackageNames: ['p-limit'], allowedVersions: '<4.0.0', }, + { + matchPackageNames: ['p-throttle'], + allowedVersions: '<4.0.0', + }, { matchPackageNames: ['p-queue'], allowedVersions: '<7.0.0', diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index 94b5e10578..bb99d9cb97 100644 --- a/plugins/notifications-backend-module-email/package.json +++ b/plugins/notifications-backend-module-email/package.json @@ -45,7 +45,7 @@ "@backstage/types": "workspace:^", "lodash": "^4.17.21", "nodemailer": "^6.9.13", - "p-throttle": "^6.1.0" + "p-throttle": "^4.1.1" }, "devDependencies": { "@backstage/backend-test-utils": "workspace:^", diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts index 3af1e498ca..11eef9b999 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts @@ -52,7 +52,7 @@ describe('NotificationsEmailProcessor', () => { notifications: { processors: { email: { - transport: { + transportConfig: { transport: 'smtp', hostname: 'localhost', port: 465, @@ -98,7 +98,7 @@ describe('NotificationsEmailProcessor', () => { notifications: { processors: { email: { - transport: { + transportConfig: { transport: 'ses', region: 'us-west-2', }, @@ -138,7 +138,7 @@ describe('NotificationsEmailProcessor', () => { notifications: { processors: { email: { - transport: { + transportConfig: { transport: 'sendmail', path: '/usr/local/bin/sendmail', }, @@ -189,7 +189,7 @@ describe('NotificationsEmailProcessor', () => { notifications: { processors: { email: { - transport: { + transportConfig: { transport: 'sendmail', path: '/usr/local/bin/sendmail', }, @@ -246,7 +246,7 @@ describe('NotificationsEmailProcessor', () => { notifications: { processors: { email: { - transport: { + transportConfig: { transport: 'sendmail', path: '/usr/local/bin/sendmail', }, @@ -306,7 +306,7 @@ describe('NotificationsEmailProcessor', () => { notifications: { processors: { email: { - transport: { + transportConfig: { transport: 'sendmail', path: '/usr/local/bin/sendmail', }, diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts index 5387d09c3d..ce944d9592 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts @@ -62,7 +62,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { const emailProcessorConfig = config.getConfig( 'notifications.processors.email', ); - this.transportConfig = emailProcessorConfig.getConfig('transport'); + this.transportConfig = emailProcessorConfig.getConfig('transportConfig'); this.broadcastConfig = emailProcessorConfig.getOptionalConfig('broadcastConfig'); this.sender = emailProcessorConfig.getString('sender'); diff --git a/yarn.lock b/yarn.lock index 3cf5a79175..6b4def9d8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6193,7 +6193,7 @@ __metadata: "@types/nodemailer": ^6.4.14 lodash: ^4.17.21 nodemailer: ^6.9.13 - p-throttle: ^6.1.0 + p-throttle: ^4.1.1 languageName: unknown linkType: soft @@ -33449,10 +33449,10 @@ __metadata: languageName: node linkType: hard -"p-throttle@npm:^6.1.0": - version: 6.1.0 - resolution: "p-throttle@npm:6.1.0" - checksum: c1947cca8844564c3d86f8c09067add5e7398b87898cb1f1aea5c48d2c211590d039a316d28a4b0d95364ffa0213c01dff6bf71175c468eab0e381f77715dbdb +"p-throttle@npm:^4.1.1": + version: 4.1.1 + resolution: "p-throttle@npm:4.1.1" + checksum: fe8709f3c3b1da7c033479375c2c302e80c1a5d86449013afa7cd46d1dc210bc824a7e4a9d088e66d31987d00878c2b5491bb2fe76246d4d2fc9a1636f5f8298 languageName: node linkType: hard From 6a8e728cf2686dbde09c0b58eb66e53ccbf76c4f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 2 May 2024 13:38:53 +0200 Subject: [PATCH 62/70] docs/architecture-overview: update to include new backend system Signed-off-by: Patrik Oldsberg --- .../package-architecture.drawio.svg | 663 ++++++++++-------- 1 file changed, 369 insertions(+), 294 deletions(-) diff --git a/docs/assets/architecture-overview/package-architecture.drawio.svg b/docs/assets/architecture-overview/package-architecture.drawio.svg index df8c0f8820..89fab2647d 100644 --- a/docs/assets/architecture-overview/package-architecture.drawio.svg +++ b/docs/assets/architecture-overview/package-architecture.drawio.svg @@ -1,69 +1,66 @@ - + - - - - - - - - - - + + + + + + + + + - +
-
-
+
+
app
- + app - - - - - - + + + + - +
-
-
+
+
backend
- + backend - - - - - - + + + + + + - +
-
-
+
+
plugin-<plugin-id>
@@ -74,19 +71,19 @@ - - - - - - + + + + + + - +
-
-
+
+
plugin-<plugin-id>-backend
@@ -97,69 +94,9 @@ - - - - - - Backend Libraries - - - - - - -
-
-
- @backstage/backend-common -
-
-
-
- - @backstage/backend-common - -
-
- - - - -
-
-
- @backstage/backend-test-utils -
-
-
-
- - @backstage/backend-test-utils - -
-
- - - - -
-
-
- @backstage/backend-tasks -
-
-
-
- - @backstage/backend-tasks - -
-
- - - - + + + Common Libraries @@ -167,16 +104,16 @@ - +
-
-
+
+
@backstage/catalog-client
- + @backstage/catalog-client @@ -184,16 +121,16 @@ - +
-
-
+
+
@backstage/types
- + @backstage/types @@ -201,16 +138,16 @@ - +
-
-
+
+
@backstage/config
- + @backstage/config @@ -218,16 +155,16 @@ - +
-
-
+
+
@backstage/errors
- + @backstage/errors @@ -235,16 +172,16 @@ - +
-
-
+
+
@backstage/catalog-model
- + @backstage/catalog-model @@ -252,24 +189,23 @@ - +
-
-
+
+
@backstage/integration
- + @backstage/integration - - - - + + + Frontend App Core @@ -277,16 +213,16 @@ - +
-
-
+
+
@backstage/core-app-api
- + @backstage/core-app-api @@ -294,36 +230,36 @@ - +
-
-
+
+
@backstage/app-defaults
- + @backstage/app-defaults - - - - - - - - - + + + + + + + + + - +
-
-
+
+
plugin-<plugin-id>-backend-module-<module-id>
@@ -334,17 +270,17 @@ - - - - + + + + - +
-
-
+
+
plugin-<plugin-id>-module-<module-id>
@@ -355,10 +291,9 @@ - - - - + + + Common Tooling @@ -366,28 +301,27 @@ - +
-
-
+
+
@backstage/cli
- + @backstage/cli - - - - - - - - + + + + + + + External Plugin Libraries @@ -395,16 +329,16 @@ - +
-
-
+
+
plugin-<other-plugin-id>-react
- + plugin-<other-plugin-id>-react @@ -412,16 +346,16 @@ - +
-
-
+
+
plugin-<other-plugin-id>-common
- + plugin-<other-plugin-id>-common @@ -429,30 +363,29 @@ - +
-
-
+
+
plugin-<other-plugin-id>-node
- + plugin-<other-plugin-id>-node - - - - - - - - - - + + + + + + + + + Plugin Libraries @@ -460,10 +393,10 @@ - +
-
-
+
+
plugin-<plugin-id>-react
@@ -477,10 +410,10 @@ - +
-
-
+
+
plugin-<plugin-id>-common
@@ -494,10 +427,10 @@ - +
-
-
+
+
plugin-<plugin-id>-node
@@ -508,13 +441,11 @@ - - - - - - - + + + + + Frontend Plugin Core @@ -522,16 +453,16 @@ - +
-
-
+
+
@backstage/core-plugin-api
- + @backstage/core-plugin-api @@ -539,16 +470,16 @@ - +
-
-
+
+
@backstage/test-utils
- + @backstage/test-utils @@ -556,24 +487,23 @@ - +
-
-
+
+
@backstage/dev-utils
- + @backstage/dev-utils - - - - + + + Frontend Libraries @@ -581,16 +511,16 @@ - +
-
-
+
+
@backstage/integration-react
- + @backstage/integration-react @@ -598,16 +528,16 @@ - +
-
-
+
+
@backstage/core-components
- + @backstage/core-components @@ -615,131 +545,276 @@ - +
-
-
+
+
@backstage/theme
- + @backstage/theme - - - - + + + + - -
-
-
+ +
+
+
Frontend Package
- + Frontend Package - -
-
-
+ +
+
+
Isomorphic Package
- + Isomorphic Package - -
-
-
+ +
+
+
Backend Package
- + Backend Package - + - -
-
-
+ +
+
+
CLI Package
- + CLI Package - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - -
-
-
+ +
+
+
Compatibility
- + Compatibility - - - - + + + + + + + + Backend App Core + + + + + + +
+
+
+ @backstage/backend-app-api +
+
+
+
+ + @backstage/backend-app-api + +
+
+ + + + +
+
+
+ @backstage/backend-defaults +
+
+
+
+ + @backstage/backend-defaults + +
+
+ + + + + + + + + Backend Plugin Core + + + + + + +
+
+
+ @backstage/backend-plugin-api +
+
+
+
+ + @backstage/backend-plugin-api + +
+
+ + + + +
+
+
+ @backstage/backend-test-utils +
+
+
+
+ + @backstage/backend-test-utils + +
+
+ + + + +
+
+
+ @backstage/backend-dev-utils +
+
+
+
+ + @backstage/backend-dev-utils + +
+
+ + + + + Backend Libraries + + + + + + +
+
+
+ @backstage/backend-tasks +
+
+
+
+ + @backstage/backend-tasks + +
+
+ + + + +
+
+
+ @backstage/backend-openapi-utils +
+
+
+
+ + @backstage/backend-openapi-utils + +
+
+ + - Viewer does not support full SVG 1.1 + Text is not SVG - cannot display From ca7ba6694d65392e36a04fa4ffe61ae2dc6e29ec Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 2 May 2024 13:39:11 +0200 Subject: [PATCH 63/70] docs/versioning-policy: update to include new backend system Signed-off-by: Patrik Oldsberg --- docs/overview/versioning-policy.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/docs/overview/versioning-policy.md b/docs/overview/versioning-policy.md index 0b1bc975bd..2ae73e1383 100644 --- a/docs/overview/versioning-policy.md +++ b/docs/overview/versioning-policy.md @@ -81,19 +81,21 @@ In order for Backstage to function properly the following versioning rules must be followed. The rules are referring to the [Package Architecture](https://backstage.io/docs/overview/architecture-overview#package-architecture). -- The versions of all the packages in the `Frontend App Core` must be from the - same release, and it is recommended to keep `Common Tooling` on that release - too. -- The Backstage dependencies of any given plugin should be from the same - release. This includes the packages from `Common Libraries`, - `Frontend Plugin Core`, and `Frontend Libraries`, or alternatively the - `Backend Libraries`. -- There must be no package that is from a newer release than the - `Frontend App Core` packages in the app. +- The versions of all packages for each of the "App Core" groups must be from the + same Backstage release. +- For each frontend and backend setup, the "App Core" packages must be ahead of or on the same Backstage release as the "Plugin Core" packages, including transitive dependencies of all installed plugins and modules. +- For any given plugin, the versions of all packages from the "Plugin Core" and + "Library" groups must be from the same Backstage release. - Frontend plugins with a corresponding backend plugin should be from the same release. The update to the backend plugin **MUST** be deployed before or together with the update to the frontend plugin. +It is allowed and often expected that the "Plugin Core" and "Library" packages +are from older releases than the "App Core" packages. It is also allowed to have +duplicate installations of the "Plugin Core" and "Library" packages. This is all +to make sure that upgrading Backstage is as smooth as possible and allows for +more flexibility across the entire plugin ecosystem. + ## Package Versioning Policy Every individual package is versioned according to [semver](https://semver.org). From 483a4f9425336091f53f79a6d6037a12dfba3f01 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 2 May 2024 13:59:48 +0200 Subject: [PATCH 64/70] microsite/data: update plugin link Signed-off-by: Patrik Oldsberg --- microsite/data/plugins/betterscan.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/betterscan.yaml b/microsite/data/plugins/betterscan.yaml index 92d1eaa7b2..852ae0081c 100644 --- a/microsite/data/plugins/betterscan.yaml +++ b/microsite/data/plugins/betterscan.yaml @@ -4,7 +4,7 @@ author: Marcin Kozlowski authorUrl: https://betterscan.io category: Security description: View security scanned vulnerabilities in Code and Cloud scanned using Open Source and proprietary scanners directly in Backstage. -documentation: https://github.com/marcinguy/betterscan-ce +documentation: https://www.npmjs.com/package/@marcinguy/backstage-plugin-betterscan iconUrl: https://uploads-ssl.webflow.com/6339e3b81867539b5fe2498d/633a1643dcb06d3029867161_g4.svg npmPackageName: '@marcinguy/backstage-plugin-betterscan' addedDate: '2022-12-08' From 2a6f10d77a4d7b86f43a5d6915959093c6572970 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 2 May 2024 14:31:05 +0200 Subject: [PATCH 65/70] cli: only warn when bump fails + fix forbidden duplicate filter Signed-off-by: Patrik Oldsberg --- .changeset/kind-toes-scream.md | 5 ++ .../cli/src/commands/versions/bump.test.ts | 65 +++++++------------ packages/cli/src/commands/versions/bump.ts | 26 ++++---- packages/cli/src/commands/versions/lint.ts | 5 +- 4 files changed, 41 insertions(+), 60 deletions(-) create mode 100644 .changeset/kind-toes-scream.md diff --git a/.changeset/kind-toes-scream.md b/.changeset/kind-toes-scream.md new file mode 100644 index 0000000000..d634f17ce4 --- /dev/null +++ b/.changeset/kind-toes-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The `versions:bump` command will no longer exit with a non-zero status if the version bump fails due to forbidden duplicate package installations. It will now also provide more information about how to troubleshoot such an error. The set of forbidden duplicates has also been expanded to include all `@backstage/*-app-api` packages. diff --git a/packages/cli/src/commands/versions/bump.test.ts b/packages/cli/src/commands/versions/bump.test.ts index c8174639a6..0dfe2f61c3 100644 --- a/packages/cli/src/commands/versions/bump.test.ts +++ b/packages/cli/src/commands/versions/bump.test.ts @@ -894,30 +894,19 @@ describe('bump', () => { newVersions: [], newRanges: [ { - name: 'first-duplicate', - oldRange: 'first-duplicate', - newRange: 'first-duplicate', - oldVersion: '1.0.0', - newVersion: '2.0.0', - }, - { - name: 'second-duplicate', - oldRange: 'second-duplicate', - newRange: 'second-duplicate', - oldVersion: '1.0.0', - newVersion: '2.0.0', - }, - { - name: 'third-duplicate', - oldRange: 'third-duplicate', - newRange: 'third-duplicate', + name: '@backstage/backend-app-api', + oldRange: '^1.0.0', + newRange: '^2.0.0', oldVersion: '1.0.0', newVersion: '2.0.0', }, ], }); mockDir.setContent({ - 'yarn.lock': lockfileMock, + 'yarn.lock': `${HEADER} +"@backstage/backend-app-api@^1.0.0": + version "1.0.0" +`, 'package.json': JSON.stringify({ workspaces: { packages: ['packages/*'], @@ -928,16 +917,7 @@ describe('bump', () => { 'package.json': JSON.stringify({ name: 'a', dependencies: { - '@backstage/core': '^1.0.5', - }, - }), - }, - b: { - 'package.json': JSON.stringify({ - name: 'b', - dependencies: { - '@backstage/core': '^1.0.3', - '@backstage/theme': '^1.0.0', + '@backstage/backend-app-api': '^1.0.0', }, }), }, @@ -952,7 +932,12 @@ describe('bump', () => { res( ctx.status(200), ctx.json({ - packages: [], + packages: [ + { + name: '@backstage/backend-app-api', + version: '2.0.0', + }, + ], }), ), ), @@ -962,24 +947,20 @@ describe('bump', () => { }); expectLogsToMatch(logs, [ 'Using default pattern glob @backstage/*', - 'Checking for updates of @backstage/core', - 'Checking for updates of @backstage/theme', - 'Checking for updates of @backstage/core-api', + 'Checking for updates of @backstage/backend-app-api', + 'Checking for updates of @backstage/backend-app-api', 'Some packages are outdated, updating', - 'unlocking @backstage/core@^1.0.3 ~> 1.0.6', - 'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7', - 'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7', - 'bumping @backstage/core in a to ^1.0.6', - 'bumping @backstage/core in b to ^1.0.6', - 'bumping @backstage/theme in b to ^2.0.0', + 'bumping @backstage/backend-app-api in a to ^2.0.0', 'Running yarn install to install new versions', 'Checking for moved packages to the @backstage-community namespace...', '⚠️ The following packages may have breaking changes:', - ' @backstage/theme : 1.0.0 ~> 2.0.0', - ' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md', + ' @backstage/backend-app-api : 1.0.0 ~> 2.0.0', + ' https://github.com/backstage/backstage/blob/master/packages/backend-app-api/CHANGELOG.md', 'Version bump complete!', - 'The following packages have duplicates but have been allowed:', - 'first-duplicate, second-duplicate, third-duplicate', + ' ⚠️ Warning! ⚠️', + ' The below package(s) have incompatible duplicate installations, likely due to a bad dependency in a plugin.', + ' You can investigate this by running `yarn why `, and report the issue to the plugin maintainers.', + ' @backstage/backend-app-api', ]); }); }); diff --git a/packages/cli/src/commands/versions/bump.ts b/packages/cli/src/commands/versions/bump.ts index aad5a7831f..1544240704 100644 --- a/packages/cli/src/commands/versions/bump.ts +++ b/packages/cli/src/commands/versions/bump.ts @@ -331,24 +331,22 @@ export default async (opts: OptionValues) => { forbiddenDuplicatesFilter(name), ); if (forbiddenNewRanges.length > 0) { - throw new Error( - `Version bump failed for ${forbiddenNewRanges - .map(i => i.name) - .join(', ')}`, - ); - } - - const allowedDuplicates = result.newRanges.filter( - ({ name }) => !forbiddenDuplicatesFilter(name), - ); - - if (allowedDuplicates.length > 0) { + console.log(chalk.yellow(' ⚠️ Warning! ⚠️')); + console.log(); console.log( chalk.yellow( - 'The following packages have duplicates but have been allowed:', + ' The below package(s) have incompatible duplicate installations, likely due to a bad dependency in a plugin.', ), ); - console.log(chalk.yellow(allowedDuplicates.map(i => i.name).join(', '))); + console.log( + chalk.yellow( + ' You can investigate this by running `yarn why `, and report the issue to the plugin maintainers.', + ), + ); + console.log(); + for (const { name } of forbiddenNewRanges) { + console.log(chalk.yellow(` ${name}`)); + } } }; diff --git a/packages/cli/src/commands/versions/lint.ts b/packages/cli/src/commands/versions/lint.ts index 9466fd0ab7..bfb2a752a0 100644 --- a/packages/cli/src/commands/versions/lint.ts +++ b/packages/cli/src/commands/versions/lint.ts @@ -27,10 +27,7 @@ export const includedFilter = (name: string) => INCLUDED.some(pattern => pattern.test(name)); // Packages that are not allowed to have any duplicates -const FORBID_DUPLICATES = [ - /^@backstage\/core-app-api$/, - /^@backstage\/plugin-/, -]; +const FORBID_DUPLICATES = [/^@backstage\/\w+-app-api$/, /^@backstage\/plugin-/]; // There are some packages that ARE explicitly allowed to have duplicates since // they handle that appropriately. This takes precedence over FORBID_DUPLICATES From e538b100433070d3dd6f06c3a7a115047ac3c02b Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Tue, 30 Apr 2024 09:50:26 +0300 Subject: [PATCH 66/70] feat: support relative notification links sent via email processor Signed-off-by: Heikki Hellgren --- .changeset/giant-donkeys-talk.md | 5 + .../NotificationsEmailProcessor.test.ts | 225 ++++++++++++------ .../processor/NotificationsEmailProcessor.ts | 38 ++- 3 files changed, 188 insertions(+), 80 deletions(-) create mode 100644 .changeset/giant-donkeys-talk.md diff --git a/.changeset/giant-donkeys-talk.md b/.changeset/giant-donkeys-talk.md new file mode 100644 index 0000000000..62af2eb7e8 --- /dev/null +++ b/.changeset/giant-donkeys-talk.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +--- + +Support relative links in notifications sent via email diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts index 11eef9b999..cdc7b9059a 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts @@ -30,6 +30,36 @@ jest.mock('nodemailer', () => ({ createTransport: jest.fn(), })); +const DEFAULT_ENTITIES_RESPONSE = { + items: [ + { + kind: 'User', + spec: { + profile: { + email: 'mock@backstage.io', + }, + }, + }, + ], +}; + +const DEFAULT_SENDMAIL_CONFIG = { + app: { + baseUrl: 'https://example.org', + }, + notifications: { + processors: { + email: { + transportConfig: { + transport: 'sendmail', + path: '/usr/local/bin/sendmail', + }, + sender: 'backstage@backstage.io', + }, + }, + }, +}; + describe('NotificationsEmailProcessor', () => { const logger = mockServices.logger.mock(); const auth = mockServices.auth(); @@ -49,6 +79,10 @@ describe('NotificationsEmailProcessor', () => { const processor = new NotificationsEmailProcessor( logger, new ConfigReader({ + app: { + baseUrl: 'http://localhost:3000', + externalBaseUrl: 'https://example.org', + }, notifications: { processors: { email: { @@ -95,6 +129,10 @@ describe('NotificationsEmailProcessor', () => { const processor = new NotificationsEmailProcessor( logger, new ConfigReader({ + app: { + baseUrl: 'http://localhost:3000', + externalBaseUrl: 'https://example.org', + }, notifications: { processors: { email: { @@ -135,6 +173,10 @@ describe('NotificationsEmailProcessor', () => { const processor = new NotificationsEmailProcessor( logger, new ConfigReader({ + app: { + baseUrl: 'http://localhost:3000', + externalBaseUrl: 'https://example.org', + }, notifications: { processors: { email: { @@ -175,29 +217,10 @@ describe('NotificationsEmailProcessor', () => { it('should send user email', async () => { (createTransport as jest.Mock).mockReturnValue(mockTransport); - getEntityRefMock.mockResolvedValue({ - kind: 'User', - spec: { - profile: { - email: 'mock@backstage.io', - }, - }, - }); + getEntityRefMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE.items[0]); const processor = new NotificationsEmailProcessor( logger, - new ConfigReader({ - notifications: { - processors: { - email: { - transportConfig: { - transport: 'sendmail', - path: '/usr/local/bin/sendmail', - }, - sender: 'backstage@backstage.io', - }, - }, - }, - }), + mockServices.rootConfig({ data: DEFAULT_SENDMAIL_CONFIG }), mockCatalogClient as unknown as CatalogClient, auth, ); @@ -218,41 +241,29 @@ describe('NotificationsEmailProcessor', () => { expect(sendmailMock).toHaveBeenCalledWith({ from: 'backstage@backstage.io', - html: '

', + html: '

https://example.org/notifications

', replyTo: undefined, subject: 'notification', - text: '', + text: 'https://example.org/notifications', to: 'mock@backstage.io', }); }); it('should send email to all', async () => { (createTransport as jest.Mock).mockReturnValue(mockTransport); - getEntitiesMock.mockResolvedValue({ - items: [ - { - kind: 'User', - spec: { - profile: { - email: 'mock@backstage.io', - }, - }, - }, - ], - }); + getEntitiesMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE); const processor = new NotificationsEmailProcessor( logger, - new ConfigReader({ - notifications: { - processors: { - email: { - transportConfig: { - transport: 'sendmail', - path: '/usr/local/bin/sendmail', - }, - sender: 'backstage@backstage.io', - broadcastConfig: { - receiver: 'users', + mockServices.rootConfig({ + data: { + ...DEFAULT_SENDMAIL_CONFIG, + notifications: { + processors: { + email: { + ...DEFAULT_SENDMAIL_CONFIG.notifications.processors.email, + broadcastConfig: { + receiver: 'users', + }, }, }, }, @@ -278,42 +289,30 @@ describe('NotificationsEmailProcessor', () => { expect(sendmailMock).toHaveBeenCalledWith({ from: 'backstage@backstage.io', - html: '

', + html: '

https://example.org/notifications

', replyTo: undefined, subject: 'notification', - text: '', + text: 'https://example.org/notifications', to: 'mock@backstage.io', }); }); it('should send email to configured addresses', async () => { (createTransport as jest.Mock).mockReturnValue(mockTransport); - getEntitiesMock.mockResolvedValue({ - items: [ - { - kind: 'User', - spec: { - profile: { - email: 'mock@backstage.io', - }, - }, - }, - ], - }); + getEntitiesMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE); const processor = new NotificationsEmailProcessor( logger, - new ConfigReader({ - notifications: { - processors: { - email: { - transportConfig: { - transport: 'sendmail', - path: '/usr/local/bin/sendmail', - }, - sender: 'backstage@backstage.io', - broadcastConfig: { - receiver: 'config', - receiverEmails: ['broadcast@backstage.io'] as JsonArray, + mockServices.rootConfig({ + data: { + ...DEFAULT_SENDMAIL_CONFIG, + notifications: { + processors: { + email: { + ...DEFAULT_SENDMAIL_CONFIG.notifications.processors.email, + broadcastConfig: { + receiver: 'config', + receiverEmails: ['broadcast@backstage.io'] as JsonArray, + }, }, }, }, @@ -339,11 +338,89 @@ describe('NotificationsEmailProcessor', () => { expect(sendmailMock).toHaveBeenCalledWith({ from: 'backstage@backstage.io', - html: '

', + html: '

https://example.org/notifications

', replyTo: undefined, subject: 'notification', - text: '', + text: 'https://example.org/notifications', to: 'broadcast@backstage.io', }); }); + + it('should send email with relative link to given address', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntityRefMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE.items[0]); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ + data: DEFAULT_SENDMAIL_CONFIG, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: 'catalog/user/default/john.doe', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '

https://example.org/catalog/user/default/john.doe

', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/catalog/user/default/john.doe', + to: 'mock@backstage.io', + }); + }); + + it('should send email with absolute link to given address', async () => { + (createTransport as jest.Mock).mockReturnValue(mockTransport); + getEntityRefMock.mockResolvedValue(DEFAULT_ENTITIES_RESPONSE.items[0]); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ + data: DEFAULT_SENDMAIL_CONFIG, + }), + mockCatalogClient as unknown as CatalogClient, + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: 'https://backstage.io', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '

https://backstage.io/

', + replyTo: undefined, + subject: 'notification', + text: 'https://backstage.io/', + to: 'mock@backstage.io', + }); + }); }); diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts index ce944d9592..ecf4eb7773 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts @@ -50,6 +50,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { private readonly cacheTtl: number; private readonly concurrencyLimit: number; private readonly throttleInterval: number; + private readonly frontendBaseUrl: string; constructor( private readonly logger: LoggerService, @@ -78,6 +79,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { this.cacheTtl = cacheConfig ? durationToMilliseconds(readDurationFromConfig(cacheConfig)) : 3_600_000; + this.frontendBaseUrl = config.getString('app.baseUrl'); } private async getTransporter() { @@ -215,20 +217,44 @@ export class NotificationsEmailProcessor implements NotificationProcessor { ); } - private async sendPlainEmail(notification: Notification, emails: string[]) { + private getNotificationLink(notification: Notification) { + if (notification.payload.link) { + try { + const url = new URL(notification.payload.link, this.frontendBaseUrl); + return url.toString(); + } catch (_e) { + // noop: fallback to relative URL + } + return notification.payload.link; + } + return `${this.frontendBaseUrl}/notifications`; + } + + private getHtmlContent(notification: Notification) { const contentParts: string[] = []; if (notification.payload.description) { contentParts.push(`${notification.payload.description}`); } - if (notification.payload.link) { - contentParts.push(`${notification.payload.link}`); - } + const link = this.getNotificationLink(notification); + contentParts.push(`${link}`); + return `

${contentParts.join('
')}

`; + } + private getTextContent(notification: Notification) { + const contentParts: string[] = []; + if (notification.payload.description) { + contentParts.push(notification.payload.description); + } + contentParts.push(this.getNotificationLink(notification)); + return contentParts.join('\n\n'); + } + + private async sendPlainEmail(notification: Notification, emails: string[]) { const mailOptions = { from: this.sender, subject: notification.payload.title, - html: `

${contentParts.join('
')}

`, - text: contentParts.join('\n\n'), + html: this.getHtmlContent(notification), + text: this.getTextContent(notification), replyTo: this.replyTo, }; From 79bb100f0526888566b93b270ffb1b0ac0c9cced Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Tue, 30 Apr 2024 13:24:20 +0300 Subject: [PATCH 67/70] fix: ensure proper slashes in the email url Signed-off-by: Heikki Hellgren --- .../NotificationsEmailProcessor.test.ts | 26 +++++++++++++++++++ .../processor/NotificationsEmailProcessor.ts | 8 +++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts index cdc7b9059a..79d1110968 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts @@ -383,6 +383,32 @@ describe('NotificationsEmailProcessor', () => { text: 'https://example.org/catalog/user/default/john.doe', to: 'mock@backstage.io', }); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { + title: 'notification', + link: '/catalog/user/default/jane.doe', + }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '

https://example.org/catalog/user/default/jane.doe

', + replyTo: undefined, + subject: 'notification', + text: 'https://example.org/catalog/user/default/jane.doe', + to: 'mock@backstage.io', + }); }); it('should send email with absolute link to given address', async () => { diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts index ecf4eb7773..c06d28f903 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts @@ -219,8 +219,14 @@ export class NotificationsEmailProcessor implements NotificationProcessor { private getNotificationLink(notification: Notification) { if (notification.payload.link) { + const stripLeadingSlash = (s: string) => s.replace(/^\//, ''); + const ensureTrailingSlash = (s: string) => s.replace(/\/?$/, '/'); + try { - const url = new URL(notification.payload.link, this.frontendBaseUrl); + const url = new URL( + stripLeadingSlash(notification.payload.link), + ensureTrailingSlash(this.frontendBaseUrl), + ); return url.toString(); } catch (_e) { // noop: fallback to relative URL From 8e9727b825d401bbfbafd22785335af20a41bad7 Mon Sep 17 00:00:00 2001 From: Chap Ambrose Date: Thu, 2 May 2024 08:22:18 -0500 Subject: [PATCH 68/70] add ensureSchemaExists to config schema Signed-off-by: Chap Ambrose --- packages/backend-common/config.d.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 700d9a6c6c..bdbec3a352 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -111,6 +111,13 @@ export interface Config { * Defaults to true if unspecified. */ ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; /** * How plugins databases are managed/divided in the provided database instance. * @@ -147,6 +154,13 @@ export interface Config { * Defaults to base config if unspecified. */ ensureExists?: boolean; + /** + * Whether to ensure the given database schema exists by creating it if it does not. + * Defaults to false if unspecified. + * + * * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + */ + ensureSchemaExists?: boolean; /** * Arbitrary config object to pass to knex when initializing * (https://knexjs.org/#Installation-client). Most notable is the From 99fe60b13e017bdb73a073b554e1891a3e76d12c Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 2 May 2024 16:01:08 +0200 Subject: [PATCH 69/70] Apply suggestions from code review Signed-off-by: Patrik Oldsberg --- packages/backend-common/config.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index bdbec3a352..c6263cde97 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -115,7 +115,7 @@ export interface Config { * Whether to ensure the given database schema exists by creating it if it does not. * Defaults to false if unspecified. * - * * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema */ ensureSchemaExists?: boolean; /** @@ -158,7 +158,7 @@ export interface Config { * Whether to ensure the given database schema exists by creating it if it does not. * Defaults to false if unspecified. * - * * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema + * NOTE: Currently only supported by the `pg` client when pluginDivisionMode: schema */ ensureSchemaExists?: boolean; /** From 8ade1283af574c503d1bf98db642eaa39f6142bb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 May 2024 19:48:57 +0000 Subject: [PATCH 70/70] chore(deps): update actions/checkout action to v4.1.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/api-breaking-changes.yml | 2 +- .github/workflows/automate_changeset_feedback.yml | 2 +- .github/workflows/automate_merge_message.yml | 2 +- .github/workflows/ci.yml | 6 +++--- .github/workflows/deploy_docker-image.yml | 2 +- .github/workflows/deploy_microsite.yml | 2 +- .github/workflows/deploy_nightly.yml | 2 +- .github/workflows/deploy_packages.yml | 4 ++-- .github/workflows/scorecard.yml | 2 +- .github/workflows/sync_code-formatting.yml | 2 +- .github/workflows/sync_dependabot-changesets.yml | 2 +- .github/workflows/sync_release-manifest.yml | 4 ++-- .github/workflows/sync_renovate-changesets.yml | 2 +- .github/workflows/sync_snyk-github-issues.yml | 2 +- .github/workflows/sync_snyk-monitor.yml | 2 +- .github/workflows/sync_version-packages.yml | 2 +- .github/workflows/uffizzi-build.yml | 4 ++-- .github/workflows/verify_accessibility.yml | 2 +- .github/workflows/verify_codeql.yml | 2 +- .github/workflows/verify_docs-quality.yml | 2 +- .github/workflows/verify_e2e-kubernetes.yml | 2 +- .github/workflows/verify_e2e-linux.yml | 2 +- .github/workflows/verify_e2e-techdocs.yml | 2 +- .github/workflows/verify_e2e-windows.yml | 2 +- .github/workflows/verify_fossa.yml | 2 +- .github/workflows/verify_microsite.yml | 2 +- .github/workflows/verify_microsite_accessibility.yml | 2 +- .github/workflows/verify_storybook.yml | 2 +- .github/workflows/verify_windows.yml | 2 +- 29 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 07206718bb..901516305d 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -18,7 +18,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: # Fetch the commit that's merged into the base rather than the target ref # This will let us diff only the contents of the PR, without fetching more history diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 4d965499d2..fefc73bd9f 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -27,7 +27,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: # Fetch the commit that's merged into the base rather than the target ref # This will let us diff only the contents of the PR, without fetching more history diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index 38243ee0ac..d6520d16a6 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: ref: '${{ github.event.pull_request.merge_commit_sha }}' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fdd31b2d57..c819ae8130 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -68,7 +68,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -197,7 +197,7 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: fetch master branch run: git fetch origin master diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 020254c872..8c263fdd4f 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -25,7 +25,7 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: path: backstage ref: ${{ github.event.client_payload.version && env.RELEASE_VERSION || github.ref }} diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 6864ec67dc..b955b39b59 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index 07c3db0f11..a3a1b5e283 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index f9ef4c67eb..0c44d32384 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -65,7 +65,7 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -148,7 +148,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 67fdff4020..5acd11c9b0 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -34,7 +34,7 @@ jobs: egress-policy: audit - name: 'Checkout code' - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index 5dbd0361c1..a59eea9c9b 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -14,7 +14,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: # Fetch changes to previous commit - required for 'only_changed' in Prettier action fetch-depth: 0 diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index 8b3be335ed..88997ea5dd 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index b0a2499d43..1549ec0e90 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -21,7 +21,7 @@ jobs: run: npm install semver@7.3.5 fs-extra@10.0.0 @manypkg/get-packages@1.1.1 - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: path: backstage # 'v' prefix is added here for the tag, we keep it out of the manifest logic @@ -29,7 +29,7 @@ jobs: # Checkout backstage/versions into /backstage/versions, which is where store the output - name: Checkout versions - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: repository: backstage/versions path: backstage/versions diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index d02e35483c..e95a68ac2f 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index d5a4048044..bebbbc4fbe 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index e2507996b1..cb3efcfac3 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -29,7 +29,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Monitor and Synchronize Snyk Policies uses: snyk/actions/node@8349f9043a8b7f0f3ee8885bf28f0b388d2446e8 # master with: diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index b2ad80b6da..82a0eae7be 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -18,7 +18,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 20000 fetch-tags: true diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index 4a52af21bc..11a0054703 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -31,7 +31,7 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: setup-node uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 @@ -89,7 +89,7 @@ jobs: egress-policy: audit - name: Checkout git repo - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Render Compose File run: | # update image after the build above diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 1c25d709cb..b4615fee0a 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -24,7 +24,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Use Node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index 920c9bab17..60cb89a045 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -47,7 +47,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index 3550df6fc0..2eb7608ccd 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 # 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 diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index 6ff41c8b74..3c425c25e9 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -26,7 +26,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index a750dfea3b..75c7a800d8 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -45,7 +45,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Configure Git run: | diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 3bcda5f2b6..d5f2594285 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -34,7 +34,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 078ddd6855..a6fa469666 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -42,7 +42,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Configure Git run: | diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index 801c5fbd28..ccc9e4509a 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Install Fossa run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash" diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index 68378aa086..db6a3e2d40 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index e0fea1a72c..19417c5cee 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: Use Node.js 18.x uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 940c136797..6a37f5abb6 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -32,7 +32,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: fetch-depth: 0 # Required to retrieve git history diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 32b0452eec..c7ff8f5ca9 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -33,7 +33,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2