diff --git a/.changeset/clever-ties-burn.md b/.changeset/clever-ties-burn.md new file mode 100644 index 0000000000..cdd861e069 --- /dev/null +++ b/.changeset/clever-ties-burn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Prevent `.set()` to execute a request to the StorageClient if the user is `guest` diff --git a/.changeset/silly-rules-join.md b/.changeset/silly-rules-join.md new file mode 100644 index 0000000000..88d3898317 --- /dev/null +++ b/.changeset/silly-rules-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Initializes a git repository when creating an app using @packages/create-app diff --git a/.github/labeler.yml b/.github/labeler.yml index bfba5eb321..587f29efa3 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -9,7 +9,7 @@ search: - plugins/search/**/* - plugins/search-*/**/* - packages/search-*/**/* -docs-like-code: +techdocs: - plugins/techdocs/**/* - plugins/techdocs-*/**/* - packages/techdocs-*/**/* diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index ae65e90ac0..46a6227f2c 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -22,7 +22,7 @@ jobs: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.5.5 + uses: backstage/actions/yarn-install@v0.5.6 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 0eedfd9d6b..466656d6f7 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -41,6 +41,11 @@ jobs: steps: - uses: actions/checkout@v3 + - name: Configure Git + run: | + git config --global user.email noreply@backstage.io + git config --global user.name 'GitHub e2e user' + - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index e62a9040a6..4ed406f52e 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -39,7 +39,7 @@ jobs: run: yarn build - name: Install mkdocs & techdocs-core - run: python -m pip install mkdocs-techdocs-core + run: python -m pip install mkdocs-techdocs-core==1.1.6 mkdocs==1.3.1 - name: techdocs-cli e2e test working-directory: packages/techdocs-cli diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 3bc41a9eeb..4e8025f7d5 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -39,6 +39,11 @@ jobs: - uses: actions/checkout@v3 + - name: Configure Git + run: | + git config --global user.email noreply@backstage.io + git config --global user.name 'GitHub e2e user' + - name: use node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: diff --git a/contrib/scripts/upgrade-backstage-app/README.md b/contrib/scripts/upgrade-backstage-app/README.md new file mode 100644 index 0000000000..ea8b0a0e2f --- /dev/null +++ b/contrib/scripts/upgrade-backstage-app/README.md @@ -0,0 +1,30 @@ +# Upgrade Backstage App + +## Overview + +The script upgrades the version of Backstage created via the create-app plugin. It lets you resolve conflicts iteratively with your own merge [tool](https://www.git-scm.com/docs/git-mergetool). + +## Requirements + +You will need to have the following tools in your shell environment: + +- git +- curl +- jq + +## Usage + +Here's how to use the script: + +1. download the script +2. copy it in the root of your app +3. bootstrap a git repository (you may already have done so): + +```bash +git init +git add . +git commit -m "initial commit" +``` + +4. run `sh upgrade-backstage-app.sh [optional-backstage-version]` +5. resolve any conflicts iteratively diff --git a/contrib/scripts/upgrade-backstage-app/upgrade-backstage-app.sh b/contrib/scripts/upgrade-backstage-app/upgrade-backstage-app.sh new file mode 100755 index 0000000000..d68d9e31c4 --- /dev/null +++ b/contrib/scripts/upgrade-backstage-app/upgrade-backstage-app.sh @@ -0,0 +1,15 @@ +#!/bin/sh +CURRENT_VERSION="v$(cat backstage.json | jq -r '.version')" +TARGET_VERSION=${1:-"$(curl -s https://api.github.com/repos/backstage/backstage/releases/latest | jq -r '.tag_name')"} +CREATE_APP_CURRENT_VERSION=$(curl -s https://raw.githubusercontent.com/backstage/backstage/$CURRENT_VERSION/packages/create-app/package.json | jq -r '.version') +CREATE_APP_TARGET_VERSION=$(curl -s https://raw.githubusercontent.com/backstage/backstage/$TARGET_VERSION/packages/create-app/package.json | jq -r '.version') + +if [ "$CURRENT_VERSION" == "$TARGET_VERSION" ]; then + echo "Already up to date" +else + echo "Attempting upgrade from Backstage $CURRENT_VERSION (create-app $CREATE_APP_CURRENT_VERSION) to $TARGET_VERSION ($CREATE_APP_TARGET_VERSION)" + rm -rf .upgrade && mkdir .upgrade + curl -s https://raw.githubusercontent.com/backstage/upgrade-helper-diff/master/diffs/$CREATE_APP_CURRENT_VERSION..$CREATE_APP_TARGET_VERSION.diff > .upgrade/upgrade.diff + git apply -3 .upgrade/upgrade.diff + git mergetool +fi diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 0af58bc911..48305e266c 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -34,6 +34,7 @@ "dependencies": { "@backstage/cli-common": "workspace:^", "chalk": "^4.0.0", + "command-exists": "^1.2.9", "commander": "^9.1.0", "fs-extra": "10.1.0", "handlebars": "^4.7.3", @@ -43,6 +44,7 @@ }, "devDependencies": { "@backstage/cli": "workspace:^", + "@types/command-exists": "^1.2.0", "@types/fs-extra": "^9.0.1", "@types/inquirer": "^8.1.3", "@types/node": "^16.11.26", diff --git a/packages/create-app/src/createApp.test.ts b/packages/create-app/src/createApp.test.ts index 8e7b3bae7d..ec8d15ead1 100644 --- a/packages/create-app/src/createApp.test.ts +++ b/packages/create-app/src/createApp.test.ts @@ -32,6 +32,8 @@ const promptMock = jest.spyOn(inquirer, 'prompt'); const checkPathExistsMock = jest.spyOn(tasks, 'checkPathExistsTask'); const templatingMock = jest.spyOn(tasks, 'templatingTask'); const checkAppExistsMock = jest.spyOn(tasks, 'checkAppExistsTask'); +const initGitRepositoryMock = jest.spyOn(tasks, 'initGitRepository'); +const readGitConfig = jest.spyOn(tasks, 'readGitConfig'); const createTemporaryAppFolderMock = jest.spyOn( tasks, 'createTemporaryAppFolderTask', @@ -47,7 +49,7 @@ describe('command entrypoint', () => { }); }); - afterAll(() => { + afterEach(() => { mockFs.restore(); }); @@ -56,6 +58,11 @@ describe('command entrypoint', () => { name: 'MyApp', dbType: 'PostgreSQL', }); + readGitConfig.mockResolvedValue({ + name: 'git-user', + email: 'git-email', + defaultBranch: 'git-default-branch', + }); }); afterEach(() => { @@ -67,6 +74,7 @@ describe('command entrypoint', () => { await createApp(cmd); expect(checkAppExistsMock).toHaveBeenCalled(); expect(createTemporaryAppFolderMock).toHaveBeenCalled(); + expect(initGitRepositoryMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); expect(moveAppMock).toHaveBeenCalled(); expect(buildAppMock).toHaveBeenCalled(); @@ -76,6 +84,7 @@ describe('command entrypoint', () => { const cmd = { path: 'myDirectory' } as unknown as Command; await createApp(cmd); expect(checkPathExistsMock).toHaveBeenCalled(); + expect(initGitRepositoryMock).toHaveBeenCalled(); expect(templatingMock).toHaveBeenCalled(); expect(buildAppMock).toHaveBeenCalled(); }); @@ -85,4 +94,11 @@ describe('command entrypoint', () => { await createApp(cmd); expect(buildAppMock).not.toHaveBeenCalled(); }); + + it('should not call `initGitRepository` when `gitConfig` is undefined', async () => { + const cmd = {} as unknown as Command; + readGitConfig.mockResolvedValue({}); + await createApp(cmd); + expect(initGitRepositoryMock).not.toHaveBeenCalled(); + }); }); diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index 7cf589e5f8..182e043565 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -28,8 +28,12 @@ import { createTemporaryAppFolderTask, moveAppTask, templatingTask, + initGitRepository, + readGitConfig, } from './lib/tasks'; +const DEFAULT_BRANCH = 'master'; + export default async (opts: OptionValues): Promise => { /* eslint-disable-next-line no-restricted-syntax */ const paths = findPaths(__dirname); @@ -73,6 +77,8 @@ export default async (opts: OptionValues): Promise => { Task.log('Creating the app...'); try { + const gitConfig = await readGitConfig(); + if (opts.path) { // Template directly to specified path @@ -80,7 +86,10 @@ export default async (opts: OptionValues): Promise => { await checkPathExistsTask(appDir); Task.section('Preparing files'); - await templatingTask(templateDir, opts.path, answers); + await templatingTask(templateDir, opts.path, { + ...answers, + defaultBranch: gitConfig?.defaultBranch ?? DEFAULT_BRANCH, + }); } else { // Template to temporary location, and then move files @@ -91,12 +100,20 @@ export default async (opts: OptionValues): Promise => { await createTemporaryAppFolderTask(tempDir); Task.section('Preparing files'); - await templatingTask(templateDir, tempDir, answers); + await templatingTask(templateDir, tempDir, { + ...answers, + defaultBranch: gitConfig?.defaultBranch ?? DEFAULT_BRANCH, + }); Task.section('Moving to final location'); await moveAppTask(tempDir, appDir, answers.name); } + if (gitConfig?.name && gitConfig?.email) { + Task.section('Initializing git repository'); + await initGitRepository(appDir); + } + if (!opts.skipInstall) { Task.section('Building the app'); await buildAppTask(appDir); diff --git a/packages/create-app/src/lib/tasks.test.ts b/packages/create-app/src/lib/tasks.test.ts index decc89fbd4..b918fc0180 100644 --- a/packages/create-app/src/lib/tasks.test.ts +++ b/packages/create-app/src/lib/tasks.test.ts @@ -17,7 +17,8 @@ import fs from 'fs-extra'; import mockFs from 'mock-fs'; import child_process from 'child_process'; -import path from 'path'; +import path, { resolve as resolvePath } from 'path'; +import os from 'os'; import { Task, buildAppTask, @@ -26,8 +27,12 @@ import { createTemporaryAppFolderTask, moveAppTask, templatingTask, + initGitRepository, + readGitConfig, } from './tasks'; +const commandExists = jest.fn(); + jest.spyOn(Task, 'log').mockReturnValue(undefined); jest.spyOn(Task, 'error').mockReturnValue(undefined); jest.spyOn(Task, 'section').mockReturnValue(undefined); @@ -36,6 +41,12 @@ jest .mockImplementation((_a, _b, taskFunc) => taskFunc()); jest.mock('child_process'); +jest.mock( + 'command-exists', + () => + (...args: any[]) => + commandExists(...args), +); // By mocking this the filesystem mocks won't mess with reading all of the package.jsons jest.mock('./versions', () => ({ @@ -87,6 +98,14 @@ jest.mock('./versions', () => ({ })); describe('tasks', () => { + const mockExec = child_process.exec as unknown as jest.MockedFunction< + ( + command: string, + options: any, + callback: (error: null, stdout: any, stderr: any) => void, + ) => void + >; + beforeEach(() => { mockFs({ 'projects/my-module.ts': '', @@ -100,6 +119,7 @@ describe('tasks', () => { }); afterEach(() => { + mockExec.mockRestore(); mockFs.restore(); }); @@ -174,12 +194,6 @@ describe('tasks', () => { describe('buildAppTask', () => { it('should change to `appDir` and run `yarn install` and `yarn tsc`', async () => { const mockChdir = jest.spyOn(process, 'chdir'); - const mockExec = child_process.exec as unknown as jest.MockedFunction< - ( - command: string, - callback: (error: null, stdout: string, stderr: string) => void, - ) => void - >; // requires callback implementation to support `promisify` wrapper // https://stackoverflow.com/a/60579617/10044859 @@ -273,4 +287,99 @@ describe('tasks', () => { ).toContain('sqlite3"'); }); }); + + describe('readGitConfig', () => { + const tmpDir = resolvePath(os.tmpdir(), 'git-temp-dir'); + + it('should return git config if git package is installed and git credentials are set', async () => { + mockExec.mockImplementation((_command, _options, callback) => { + callback(null, { stdout: 'main' }, 'standard error'); + }); + + commandExists.mockResolvedValue(true); + + const gitConfig = await readGitConfig(); + + expect(gitConfig).toBeTruthy(); + expect(gitConfig).toEqual({ + name: 'main', + email: 'main', + defaultBranch: 'main', + }); + expect(mockExec).toHaveBeenCalledWith( + 'git config user.name', + { cwd: tmpDir }, + expect.any(Function), + ); + expect(mockExec).toHaveBeenCalledWith( + 'git config user.email', + { cwd: tmpDir }, + expect.any(Function), + ); + expect(mockExec).toHaveBeenCalledWith( + 'git init', + { cwd: tmpDir }, + expect.any(Function), + ); + expect(mockExec).toHaveBeenCalledWith( + 'git commit --allow-empty -m "Initial commit"', + { cwd: tmpDir }, + expect.any(Function), + ); + }); + + it('should return false if git package is not installed', async () => { + commandExists.mockResolvedValue(false); + + const gitConfig = await readGitConfig(); + + expect(gitConfig).toEqual({}); + }); + + it('should return false if git package is installed but git credentials are not set', async () => { + mockExec.mockImplementation((_command, _options, callback) => { + callback(null, { stdout: null }, 'standard error'); + }); + + commandExists.mockResolvedValue(true); + + const gitConfig = await readGitConfig(); + + expect(gitConfig).toEqual({}); + expect(mockExec).toHaveBeenCalledWith( + 'git config user.name', + { cwd: tmpDir }, + expect.any(Function), + ); + expect(mockExec).toHaveBeenCalledWith( + 'git config user.email', + { cwd: tmpDir }, + expect.any(Function), + ); + }); + }); + + describe('initGitRepository', () => { + it('should initialize a git repository at the given path', async () => { + const destinationDir = 'tmp/mockApp/'; + + mockExec.mockImplementation((_command, callback) => { + callback(null, { stdout: 'main' }, 'standard error'); + }); + + await initGitRepository(destinationDir); + + expect(mockExec).toHaveBeenCalledTimes(2); + expect(mockExec).toHaveBeenNthCalledWith( + 1, + 'git init', + expect.any(Function), + ); + expect(mockExec).toHaveBeenNthCalledWith( + 2, + 'git commit --allow-empty -m "Initial commit"', + expect.any(Function), + ); + }); + }); }); diff --git a/packages/create-app/src/lib/tasks.ts b/packages/create-app/src/lib/tasks.ts index 1650432969..1e30f9d33a 100644 --- a/packages/create-app/src/lib/tasks.ts +++ b/packages/create-app/src/lib/tasks.ts @@ -28,10 +28,18 @@ import { import { exec as execCb } from 'child_process'; import { packageVersions } from './versions'; import { promisify } from 'util'; +import commandExists from 'command-exists'; +import os from 'os'; const TASK_NAME_MAX_LENGTH = 14; const exec = promisify(execCb); +export type GitConfig = { + name?: string; + email?: string; + defaultBranch?: string; +}; + export class Task { static log(name: string = '') { process.stdout.write(`${chalk.green(name)}\n`); @@ -236,3 +244,77 @@ export async function moveAppTask( }); }); } + +/** + * Read git configs by creating a temp folder and initializing a repo + * + * @throws if `exec` fails + */ +export async function readGitConfig(): Promise { + const tempDir = resolvePath(os.tmpdir(), 'git-temp-dir'); + + const runCmd = (cmd: string) => + exec(cmd, { cwd: tempDir }).catch(error => { + process.stdout.write(error.stderr); + process.stdout.write(error.stdout); + throw new Error(`Could not execute command ${chalk.cyan(cmd)}`); + }); + + const isGitAvailable = await commandExists('git').catch(() => false); + + if (!isGitAvailable) return {}; + + try { + await fs.mkdir(tempDir); + + const [gitUsername, gitEmail] = await Promise.all([ + runCmd('git config user.name'), + runCmd('git config user.email'), + ]); + + const gitCredentials = Boolean( + gitUsername.stdout?.trim() && gitEmail.stdout?.trim(), + ); + + if (!gitCredentials) return {}; + + await runCmd('git init'); + await runCmd('git commit --allow-empty -m "Initial commit"'); + + const gitDefaultBranch = await runCmd( + 'git branch --format="%(refname:short)"', + ); + + return { + name: gitUsername.stdout?.trim(), + email: gitEmail.stdout?.trim(), + defaultBranch: gitDefaultBranch.stdout?.trim(), + }; + } catch (error) { + throw new Error(`Failed to read git config, ${error}`); + } finally { + await fs.rm(tempDir, { recursive: true }); + } +} + +/** + * Initializes a git repository in the destination folder + * + * @param dir - source path to initialize git repository in + * @throws if `exec` fails + */ +export async function initGitRepository(dir: string) { + const runCmd = (cmd: string) => + exec(cmd).catch(error => { + process.stdout.write(error.stderr); + process.stdout.write(error.stdout); + throw new Error(`Could not execute command ${chalk.cyan(cmd)}`); + }); + + await Task.forItem('init', 'git repository', async () => { + process.chdir(dir); + + await runCmd('git init'); + await runCmd('git commit --allow-empty -m "Initial commit"'); + }); +} diff --git a/packages/create-app/templates/default-app/package.json.hbs b/packages/create-app/templates/default-app/package.json.hbs index dd4d937db0..c5810d8880 100644 --- a/packages/create-app/templates/default-app/package.json.hbs +++ b/packages/create-app/templates/default-app/package.json.hbs @@ -16,7 +16,7 @@ "clean": "backstage-cli repo clean", "test": "backstage-cli repo test", "test:all": "backstage-cli repo test --coverage", - "lint": "backstage-cli repo lint --since origin/master", + "lint": "backstage-cli repo lint --since origin/{{defaultBranch}}", "lint:all": "backstage-cli repo lint", "prettier:check": "prettier --check .", "create-plugin": "backstage-cli create-plugin --scope internal", diff --git a/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts b/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts index 0ebd96d19c..a421c413dc 100644 --- a/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts +++ b/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts @@ -38,6 +38,10 @@ describe('Persistent Storage API', () => { const mockIdentityApi: Partial = { getCredentials: async () => ({ token: 'a-token' }), }; + const mockIdentityApiFallback: Partial = { + // This API recreates the guest mode, where the WebStorage is used as fallback + getCredentials: async () => ({}), + }; const createPersistentStorage = ( args?: Partial<{ @@ -56,6 +60,23 @@ describe('Persistent Storage API', () => { }); }; + const createPersistentStorageFallback = ( + args?: Partial<{ + fetchApi: FetchApi; + discoveryApi: DiscoveryApi; + errorApi: ErrorApi; + namespace?: string; + }>, + ): StorageApi => { + return UserSettingsStorage.create({ + errorApi: mockErrorApi, + fetchApi: new MockFetchApi(), + discoveryApi: mockDiscoveryApi, + identityApi: mockIdentityApiFallback as IdentityApi, + ...args, + }); + }; + afterEach(() => { jest.resetAllMocks(); }); @@ -124,6 +145,33 @@ describe('Persistent Storage API', () => { await storage.set('my-key', dummyValue); }); + it('should fallback set when user not logged in', async () => { + const storage = createPersistentStorageFallback(); + + const selectedKeyNextHandler = jest.fn(); + const dummyValue = 'my-value'; + + await new Promise(resolve => { + storage.observe$('my-key').subscribe({ + next: snapshot => { + selectedKeyNextHandler(snapshot); + if (snapshot.presence === 'present') { + resolve(); + } + }, + }); + + storage.set('my-key', dummyValue); + }); + + expect(selectedKeyNextHandler).toHaveBeenCalledTimes(1); + expect(selectedKeyNextHandler).toHaveBeenCalledWith({ + key: 'my-key', + value: dummyValue, + presence: 'present', + }); + }); + it('should subscribe to key changes when setting a new value', async () => { const storage = createPersistentStorage(); diff --git a/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.ts b/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.ts index 8905b4aeea..ef4d3ead5d 100644 --- a/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.ts +++ b/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.ts @@ -118,6 +118,7 @@ export class UserSettingsStorage implements StorageApi { if (!(await this.isSignedIn())) { await this.fallback.set(key, data); this.notifyChanges({ key, presence: 'present', value: data }); + return; } const fetchUrl = await this.getFetchUrl(key); diff --git a/yarn.lock b/yarn.lock index 7e67c7174a..1178b3d940 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3637,11 +3637,13 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/cli-common": "workspace:^" + "@types/command-exists": ^1.2.0 "@types/fs-extra": ^9.0.1 "@types/inquirer": ^8.1.3 "@types/node": ^16.11.26 "@types/recursive-readdir": ^2.2.0 chalk: ^4.0.0 + command-exists: ^1.2.9 commander: ^9.1.0 fs-extra: 10.1.0 handlebars: ^4.7.3 @@ -11140,8 +11142,8 @@ __metadata: linkType: hard "@octokit/auth-app@npm:^4.0.0": - version: 4.0.5 - resolution: "@octokit/auth-app@npm:4.0.5" + version: 4.0.6 + resolution: "@octokit/auth-app@npm:4.0.6" dependencies: "@octokit/auth-oauth-app": ^5.0.0 "@octokit/auth-oauth-user": ^2.0.0 @@ -11153,7 +11155,7 @@ __metadata: lru-cache: ^6.0.0 universal-github-app-jwt: ^1.0.1 universal-user-agent: ^6.0.0 - checksum: 26da7c1b279be68be17572897652091257a5ef7666fa5ac4c79dd42f2717aed560b117089c44ad766b4f90a87348572356ac3571c417fef385fdf31794281023 + checksum: 342fece6db4470ee489e710af8aa14ffd3a89e666815a15fadbabe3a382932a714d5bece24375f0ca39f3310f9d10bd28bc348210282cbc6701d27dd9b2ff7ed languageName: node linkType: hard @@ -13797,11 +13799,11 @@ __metadata: linkType: hard "@types/ldapjs@npm:^2.2.0": - version: 2.2.2 - resolution: "@types/ldapjs@npm:2.2.2" + version: 2.2.4 + resolution: "@types/ldapjs@npm:2.2.4" dependencies: "@types/node": "*" - checksum: e5da993f06240251dad1e095564c722124ed08d915781fb3046f7933cde94b5d2681030ab9f48d4aa5d72b655b501bfd6f2ad4ea41d557d0e1a08d574ed058bf + checksum: 3f240809927e1292380e5977579d7edffb6e5e41b35510c7091b51da19e7387d4ebff0f254c0b145f3cf5d5485b6bb39c77335d857d092e6f4e9ce5a6682a872 languageName: node linkType: hard @@ -15319,12 +15321,12 @@ __metadata: linkType: hard "@yarnpkg/parsers@npm:^3.0.0-rc.4": - version: 3.0.0-rc.20 - resolution: "@yarnpkg/parsers@npm:3.0.0-rc.20" + version: 3.0.0-rc.21 + resolution: "@yarnpkg/parsers@npm:3.0.0-rc.21" dependencies: js-yaml: ^3.10.0 tslib: ^2.4.0 - checksum: a0dfc2d4d824322ae779363870ad7705b42161ad5e90c992404bf5dc0454a046a6f04144303c9bb047239e480b882b4105272127f2ba080b865180276c46deda + checksum: c0741ef01089a7d452dfb75b8c24a82ddcc3e218dac22b794a06f2e50c7070378c460f63f63fb7ed0f62f634114e448e3d85a75ccd4db84e1526242ae9b4a7d5 languageName: node linkType: hard @@ -18753,9 +18755,9 @@ __metadata: linkType: hard "core-js@npm:^3.6.5": - version: 3.25.2 - resolution: "core-js@npm:3.25.2" - checksum: e93c6c645d44d98973efb07750975552ad405f080f5a563a99972ff6b2c5c6ee25705f55accd363f5dccd51e9e5f56be25e2be6c14a7294da65763e0e5659c02 + version: 3.25.3 + resolution: "core-js@npm:3.25.3" + checksum: 26ca0a5e575e6da03dc30099f48bc00db50a1c0a3f81299165f111cb163869ae453de5e345f2cb7bc8be85a9adcee11fca94f0fbdbdf6d483c68e1c55a9efc5a languageName: node linkType: hard @@ -23710,8 +23712,8 @@ __metadata: linkType: hard "google-auth-library@npm:^8.0.0": - version: 8.5.1 - resolution: "google-auth-library@npm:8.5.1" + version: 8.5.2 + resolution: "google-auth-library@npm:8.5.2" dependencies: arrify: ^2.0.0 base64-js: ^1.3.0 @@ -23722,7 +23724,7 @@ __metadata: gtoken: ^6.1.0 jws: ^4.0.0 lru-cache: ^6.0.0 - checksum: 0d80d7e136e672055e44fdb44f9e13843a554cc531a01deb1b0da6b78427778785faa7377ed85d840208bb61878cd7d69902172886476bd49ea67e645729570a + checksum: 5ab2904f5da3c119a7c241a1d5a11640468a7da58dfcec8a9cad181cc2723e6662b3c65997906069852d9fa066ba3e4b7f14e11cbb80d541e320b66ead6777dc languageName: node linkType: hard @@ -27722,8 +27724,8 @@ __metadata: linkType: hard "ldapjs@npm:^2.2.0": - version: 2.3.2 - resolution: "ldapjs@npm:2.3.2" + version: 2.3.3 + resolution: "ldapjs@npm:2.3.3" dependencies: abstract-logging: ^2.0.0 asn1: ^0.2.4 @@ -27733,7 +27735,7 @@ __metadata: once: ^1.4.0 vasync: ^2.2.0 verror: ^1.8.1 - checksum: 4eb7b18839d8fe1d8acc0ba5d2aa71aefcb25ecb6f4f9582299d73faf546984326590c44366f8b148d3a4276707006b1aaffffdc548b3c4ca753e720403da4a5 + checksum: 791cf22812805db3888dfc8aa8e4db8337e67387b318522c9965d40e678ecdb64950a6ea5930083590ec3377038620a3e63f90e985173ae6aca40e7f485290e0 languageName: node linkType: hard