From 76b5f4d757d382c9070185e37b161d96d2f44cf8 Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Wed, 23 Oct 2024 13:44:59 -0400 Subject: [PATCH 1/6] added script to migrate janus plugins Signed-off-by: Kashish Mittal --- CONTRIBUTING.md | 54 ++ .../packages/cli/src/commands/index.ts | 16 + .../src/commands/plugin/janus-migration.ts | 607 ++++++++++++++++++ 3 files changed, 677 insertions(+) create mode 100644 workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 136c6f71a..1e8a9ae17 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,6 +18,9 @@ The `redhat-developer/rhdh-plugins` repository is designed as a collaborative sp - [Creating new plugins or packages in a Workspace](#creating-new-plugins-or-packages-in-a-workspace) - [Migrating a plugin](#migrating-a-plugin) - [Manual migration steps](#manual-migration-steps) + - [Using the cli to migrate plugins from janus-idp/backstage-plugins](#using-the-cli-to-migrate-plugins-from-janus-idpbackstage-plugins) + - [Prerequisites](#prerequisites) + - [Steps](#steps) - [API Reports](#api-reports) - [Submitting a Pull Request](#submitting-a-pull-request) @@ -183,6 +186,57 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ 9. In the original repository, update the plugin to indicate that it has been moved to the `redhat-developer/rhdh-plugins` repository. You may wish to deprecate the old version on npm. +### Using the cli to migrate plugins from janus-idp/backstage-plugins + +## Prerequisites + +- Have a `janus-idp/backstage-plugins` fork locally cloned +- Have a `backstage/rhdh-plugins` fork locally cloned + +## Steps + +1. In both repositories, create a new branch: + + - For `janus-idp/backstage-plugins`: + + ```bash + git checkout -b "deprecate-workspace-name" + ``` + + - For `backstage/rhdh-plugins`: + ```bash + git checkout -b "migrate-workspace-name" + ``` + +2. In the `backstage/rhdh-plugins` repository, execute the janus-plugin migrate command.- Usage:`yarn rhdh-cli janus-plugin migrate --monorepo-path [path_to_backstage_plugins]--workspace-name [workspace_name] --branch [branch_name] --maintainers [maintainer1],[maintainer2],[maintainer3],...` + + - The `path_to_backstage_plugins` is the path to the `backstage-plugins` project where the plugin(s) you want to migrate live. + - The `workspace-name` is the name of the workspace you wish to create in the `rhdh-plugins` project. All plugins in the `backstage-plugins` that either are exactly or start with `@janus-idp/backstage-plugin-[workspace_name]` will be migrated to this new workspace. + - The `branch_name` is the name of the branch in the `backstage-plugins` project where the changes to add a deprecate note for the migrated plugins will be made. + - The `maintainers` array of arguments is the github usernames of those individuals that should be listed as the maintainers for the migrated plugins. Please separate each maintainer by a comma while supplying this value. + + - example usage: + ```bash + yarn rhdh-cli janus-plugin migrate --monorepo-path ../backstage-plugins --workspace-name workspace-name --branch deprecate-workspace-name --maintainers @maintainer1,@maintainer2,@maintainer3 + ``` + +3. > [!Important] + > This script updates metadata commonly found across all plugins. Please review your migrated plugins to ensure that all references to "janus" have been updated to point to "rhdh-plugins." + + The script will generate changesets in both repositories. Be sure to commit these changes and open pull requests. + +4. If you run into CI issues take a look at [this github gist](https://gist.github.com/Fortune-Ndlovu/1562789f3905b4fe818b9079a3032982) which outlines the process taken to migrate argocd plugins in great detail. + +5. Check if the migrated plugins need to be added to janus-idp/backstage-showcase. If they do, create a wrapper for them following the steps below: + +- In `dynamic-plugins> wrappers` create a directory, name it based on your plugin (eg: `backstage-community-plugin-3scale-backend`) +- Create a `src` directory within it +- Add a `index.ts` file to this src directory and export from the plugin package here. Eg: `export * from '@backstage-community/plugin-3scale-backend';` +- In `dynamic-plugins> wrappers > backstage-community-plugin-3scale-backend` (or whatever you named your wrapper directory), add a `package.json` file. Add your plugin package in dependencies. + - [Frontend plugin `package.json` example](https://github.com/janus-idp/backstage-showcase/blob/main/dynamic-plugins/wrappers/backstage-community-plugin-redhat-argocd/package.json) + - [Backend plugin `package.json` example](https://github.com/janus-idp/backstage-showcase/blob/main/dynamic-plugins/wrappers/backstage-community-plugin-3scale-backend/package.json) +- run `yarn export-dynamic` to generate dist-dynamic directory + ## API Reports This repository uses [API Extractor](https://api-extractor.com/) and TSDoc comments to generate API Reports in Markdown format, similar to those used in Backstage. If you make changes to the API or add a new plugin then you will need either generate a new API Report or update an existing API Report. If you don't do this the CI build will fail when you create your Pull Request. diff --git a/workspaces/repo-tools/packages/cli/src/commands/index.ts b/workspaces/repo-tools/packages/cli/src/commands/index.ts index e3abb0143..c2e38949c 100644 --- a/workspaces/repo-tools/packages/cli/src/commands/index.ts +++ b/workspaces/repo-tools/packages/cli/src/commands/index.ts @@ -35,6 +35,22 @@ function lazy( } export const registerCommands = (program: Command) => { + program + .command('janus-plugin') + .command('migrate') + .requiredOption('--monorepo-path [path]', 'Path to the monorepo') + .requiredOption( + '--workspace-name [name]', + 'Name of the workspace that will be created, the plugins will be pulled automatically from the monorepo', + ) + .option('--branch [branch]', 'use a branch for deprecation commits') + .option('--maintainers ', 'List of maintainers', value => + value.split(','), + ) + .action( + lazy(() => import('./plugin/janus-migration').then(m => m.default)), + ); + program .command('workspace') .command('create') diff --git a/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts b/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts new file mode 100644 index 000000000..dcceaa11d --- /dev/null +++ b/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts @@ -0,0 +1,607 @@ +/* + * 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 chalk from 'chalk'; +import fs from 'fs-extra'; +import { OptionValues } from 'commander'; +import findUp from 'find-up'; +import path from 'path'; +import { getPackages, Package } from '@manypkg/get-packages'; +import { createWorkspace } from '../../lib/workspaces/createWorkspace'; +import { ExitCodeError } from '../../lib/errors'; +import { promisify } from 'util'; +import { execFile } from 'child_process'; +import semver from 'semver'; + +const replace = require('replace-in-file'); + +const exec = promisify(execFile); + +// Do some magic to get the right paths, that are idempotent regardless of where the command is run from +const getPaths = async (options: { + monorepoPath: string; + workspaceName: string; +}) => { + const rhdhPluginsPackageJson = await findUp( + async dir => { + const packageJsonPath = path.join(dir, 'package.json'); + const hasPackageJson = await findUp.exists(packageJsonPath); + if (hasPackageJson) { + const packageJsonContents = require(packageJsonPath); + if (packageJsonContents.name === '@redhat-developer/rhdh-plugins') { + return packageJsonPath; + } + } + + return undefined; + }, + { type: 'file' }, + ); + + if (!rhdhPluginsPackageJson) { + throw new Error('Could not find rhdh plugins package.json'); + } + + const rhdhPluginsRoot = path.dirname(rhdhPluginsPackageJson); + + return { + rhdhPluginsRoot, + monorepoRoot: options.monorepoPath, + workspacePath: path.join( + rhdhPluginsRoot, + 'workspaces', + options.workspaceName, + ), + }; +}; + +const getMonorepoPackagesForWorkspace = async (options: { + monorepoRoot: string; + workspaceName: string; +}) => { + const packages = await getPackages(options.monorepoRoot); + + const workspacePackages = packages.packages.filter( + pkg => + pkg.packageJson.name.startsWith( + `@janus-idp/backstage-plugin-${options.workspaceName}-`, + ) || + pkg.packageJson.name === + `@janus-idp/backstage-plugin-${options.workspaceName}`, + ); + + return workspacePackages; +}; + +const generateNewPackageName = (name: string) => + name.replace(`@janus-idp`, `@red-hat-developer-hub`); + +const ensureWorkspaceExists = async (options: { + workspacePath: string; + workspaceName: string; + rhdhPluginsRoot: string; +}) => { + // check if the workspace exists, create it if not. + const workspaceExists = await fs + .access(options.workspacePath, fs.constants.F_OK) + .then(() => true) + .catch(() => false); + + if (workspaceExists) { + console.error( + chalk.red`Workspace already exists at ${options.workspacePath}`, + ); + throw new ExitCodeError(1); + } + + console.log(chalk.yellow`Creating workspace at ${options.workspacePath}`); + + await createWorkspace({ + cwd: options.rhdhPluginsRoot, + name: options.workspaceName, + }); +}; + +const fixSourceCodeReferences = async (options: { + packagesToBeMoved: Package[]; + workspacePath: string; +}) => { + // Rename app-config.janus-idp.yaml to app-config.yaml for each package + for (const pkg of options.packagesToBeMoved) { + const oldConfigPath = path.join( + options.workspacePath, + pkg.packageJson.repository.directory, + 'app-config.janus-idp.yaml', + ); + const newConfigPath = path.join( + options.workspacePath, + pkg.packageJson.repository.directory, + 'app-config.yaml', + ); + + // Check if app-config.janus-idp.yaml.js exists + const appConfigFileExists = await fs.pathExists(oldConfigPath); + + if (appConfigFileExists) { + await fs.rename(oldConfigPath, newConfigPath); + const replacements = { + 'janus-idp': 'red-hat-developer-hub', + }; + + await replaceInFile(newConfigPath, replacements); + } + + // Update janus references in catalog-info.yaml + const catalogInfoPath = path.join( + options.workspacePath, + pkg.packageJson.repository.directory, + 'catalog-info.yaml', + ); + + // Check if catalog-info.yaml exists + const catalogInfoFileExists = await fs.pathExists(catalogInfoPath); + + if (catalogInfoFileExists) { + updateCatalogInfoYaml( + catalogInfoPath, + path.basename(options.workspacePath), + ).catch(console.error); + } + + // Update janus references in .prettierrc.js + const prettierConfigPath = path.join( + options.workspacePath, + pkg.packageJson.repository.directory, + '.prettierrc.js', + ); + + // Check if .prettierrc.js exists + const prettierConfigFileExists = await fs.pathExists(prettierConfigPath); + + if (prettierConfigFileExists) { + let prettierConfig = await fs.readFile(prettierConfigPath, 'utf-8'); + + // Replace all occurrences of @janus-idp with @red-hat-developer-hub + prettierConfig = prettierConfig.replace( + /@janus-idp/g, + '@red-hat-developer-hub', + ); + + await fs.writeFile(prettierConfigPath, prettierConfig, 'utf-8'); + } + } + + return await replace({ + files: path.join(options.workspacePath, '**', '*'), + processor: (input: string) => + options.packagesToBeMoved.reduce((acc, { packageJson }) => { + const newPackageName = generateNewPackageName(packageJson.name); + return acc.replace(new RegExp(packageJson.name, 'g'), newPackageName); + }, input), + }); +}; + +const createChangeset = async (options: { + packages: string[]; + workspacePath: string; + message: string; +}) => { + const changesetFile = path.join( + options.workspacePath, + '.changeset', + `migrate-${new Date().getTime()}.md`, + ); + + const changesetContents = `\ +--- +${options.packages.map(p => `'${p}': patch`).join('\n')} +--- + +${options.message} +`; + + await fs.appendFile(changesetFile, changesetContents); +}; +const deprecatePackage = async (options: { package: Package }) => { + const newPackageName = generateNewPackageName( + options.package.packageJson.name, + ); + + // Define the package directory + const packageDir = options.package.dir; + + // First, update the README + await fs.writeFile( + path.join(packageDir, 'README.md'), + `# Deprecated\n\nThis package has been moved to the [red-hat-developer/rhdh-plugins](https://github.com/redhat-developer/rhdh-plugins) repository. Migrate to using \`${newPackageName}\` instead.\n`, + ); + + // List all files in the directory + const files = await fs.readdir(packageDir); + + // Filter out README.md and delete the rest + await Promise.all( + files + .filter(file => file !== 'README.md') // Exclude README.md + .map(file => + fs.rm(path.join(packageDir, file), { recursive: true, force: true }), + ), + ); +}; + +// Function to replace content in the file +async function replaceInFile( + filePath: string, + replacements: Record, +) { + try { + let content = await fs.readFile(filePath, 'utf8'); + + // Perform replacements + for (const [oldValue, newValue] of Object.entries(replacements)) { + const regex = new RegExp(oldValue, 'g'); + content = content.replace(regex, newValue); + } + + // Write the updated content back to the file + await fs.writeFile(filePath, content); + } catch (err) { + console.error(`Error processing ${filePath}: ${err}`); + } +} + +// Main function to update catalog-info.yaml +async function updateCatalogInfoYaml( + catalogInfoPath: string, + workspaceName: string, +) { + const replacements = { + 'https://github.com/janus-idp/backstage-plugins/tree/main': `https://github.com/redhat-developer/rhdh-plugins/tree/main/workspaces/${workspaceName}`, + 'https://github.com/janus-idp/backstage-plugins/blob/main': `https://github.com/redhat-developer/rhdh-plugins/blob/main/workspaces/${workspaceName}`, + 'https://github.com/janus-idp/backstage-plugins/edit/main': `https://github.com/redhat-developer/rhdh-plugins/edit/main/workspaces/${workspaceName}`, + 'janus-idp/maintainers-plugins': 'rhdh/maintainers-plugins', + 'sonarqube.org/project-key: janus-idp_backstage-plugins': + 'sonarqube.org/project-key: red_hat_developer_hub_plugins', + 'janus-idp-backstage': 'red-hat-developer-hub', + 'janus-idp': 'red-hat-developer-hub', + }; + + await replaceInFile(catalogInfoPath, replacements); +} + +const packageAndTypeMap = { + lodash: { name: '@types/lodash', version: '^4.14.151' }, + recharts: { name: '@types/lodash', version: '^4.14.151' }, + uuid: { name: '@types/uuid', version: '^9.0.0' }, + 'humanize-duration': { name: '@types/humanize-duration', version: '^3.18.1' }, + 'mime-types': { name: '@types/mime-types', version: '^2.1.0' }, + supertest: { name: '@types/supertest', version: '^2.0.8' }, + '@backstage/core-components': { name: 'canvas', version: '^2.11.2' }, + pluralize: { name: '@types/pluralize', version: '^0.0.33' }, + 'git-url-parse': { name: '@types/git-url-parse', version: '^9.0.0' }, + 'node-fetch': { name: '@types/node-fetch', version: '^2.5.12' }, + dompurify: { name: '@types/dompurify', version: '^3.0.0' }, + 'react-dom': { name: '@types/react-dom', version: '^18.2.19' }, + react: { name: '@types/react', version: '^18.2.58' }, +}; + +export default async (opts: OptionValues) => { + const { monorepoPath, workspaceName, branch, maintainers } = opts as { + monorepoPath: string; + workspaceName: string; + branch?: string; + maintainers: string[]; + }; + + try { + await exec('git', ['status', '--porcelain'], { cwd: monorepoPath }); + } catch { + console.error( + chalk.red`The provided monorepo path is either not a git repository or not clean, please provide a valid monorepo path.`, + ); + process.exit(1); + } + + const { rhdhPluginsRoot, monorepoRoot, workspacePath } = await getPaths({ + monorepoPath, + workspaceName, + }); + + // find all the packages in the main monorepo that should be in the workspace + const packagesToBeMoved = await getMonorepoPackagesForWorkspace({ + monorepoRoot, + workspaceName, + }); + if (packagesToBeMoved.length === 0) { + console.error(chalk.red`No packages found for plugin ${workspaceName}`); + process.exit(1); + } + + console.log( + chalk.green`Found ${packagesToBeMoved.length} packages to be moved`, + chalk.blueBright`${packagesToBeMoved + .map(p => p.packageJson.name) + .join(', ')}`, + ); + + // Create new workspace in rhdh plugins repository + await ensureWorkspaceExists({ + workspacePath, + workspaceName, + rhdhPluginsRoot, + }); + + for (const packageToBeMoved of packagesToBeMoved) { + const newPathForPackage = path.join( + workspacePath, + packageToBeMoved.relativeDir, + ); + console.log( + chalk.yellow`Moving package ${packageToBeMoved.packageJson.name} to ${newPathForPackage}`, + ); + + // Move the code + await fs.copy(packageToBeMoved.dir, newPathForPackage); + + // Update the package.json versions to the latest published versions if not being moved across. + const movedPackageJsonPath = path.join(newPathForPackage, 'package.json'); + const movedPackageJson = await fs.readJson(movedPackageJsonPath); + + // Fix the repositories field in the new repo + movedPackageJson.repository = { + type: 'git', + url: 'https://github.com/redhat-developer/rhdh-plugins', + directory: `workspaces/${workspaceName}/${packageToBeMoved.relativeDir}`, + }; + + movedPackageJson.bugs = + 'https://github.com/redhat-developer/rhdh-plugins/issues'; + if (movedPackageJson.files) { + const updatedFiles = movedPackageJson.files.map((file: string) => + file === 'app-config.janus-idp.yaml' ? 'app-config.yaml' : file, + ); + movedPackageJson.files = updatedFiles; + } + movedPackageJson.devDependencies ??= {}; + movedPackageJson.dependencies ??= {}; + + movedPackageJson.maintainers = []; + + maintainers.forEach(maintainer => { + movedPackageJson.maintainers.push(maintainer); + }); + + // make sure to add all peerDependencies as devDeps as these won't be installed in the app any longer and tests might fail + for (const [key, value] of Object.entries( + movedPackageJson.peerDependencies ?? {}, + )) { + if (!movedPackageJson.devDependencies[key]) { + movedPackageJson.devDependencies[key] = value; + } + } + + // Add additional types that could come from elsewhere + for (const [key, value] of Object.entries(packageAndTypeMap)) { + if ( + (movedPackageJson.dependencies[key] || + movedPackageJson.devDependencies[key]) && + !movedPackageJson.devDependencies[value.name] && + !movedPackageJson.dependencies[value.name] + ) { + movedPackageJson.devDependencies[value.name] = value.version; + } + } + + // If it's a frontend package do some magic + const frontendDevDeps = { + '@testing-library/dom': '^10.0.0', + '@testing-library/jest-dom': '^6.0.0', + '@testing-library/react': '^15.0.0', + }; + + if (movedPackageJson.backstage.role === 'frontend-plugin') { + for (const [key, value] of Object.entries(frontendDevDeps)) { + movedPackageJson.devDependencies[key] = value; + } + } + + // if it's got material-ui pickers as a dep we need some other magic + const materialUiPickersDep = + movedPackageJson.dependencies['@material-ui/pickers'] || + movedPackageJson.devDependencies['@material-ui/pickers']; + if (materialUiPickersDep) { + // copy the patch + await fs.mkdirp(path.join(workspacePath, '.yarn', 'patches')); + await fs.copyFile( + path.join( + __dirname, // eslint-disable-line no-restricted-syntax + '..', + '..', + 'lib', + 'workspaces', + 'patches', + '@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch', + ), + path.join( + workspacePath, + '.yarn', + 'patches', + '@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch', + ), + ); + + const rootPackageJson = await fs.readJson( + path.join(workspacePath, 'package.json'), + ); + + rootPackageJson.resolutions ??= {}; + rootPackageJson.resolutions[ + `@material-ui/pickers@${materialUiPickersDep}` + ] = + 'patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch'; + + await fs.writeJson( + path.join(workspacePath, 'package.json'), + rootPackageJson, + { spaces: 2 }, + ); + } + + // Fix for some packages without react/react-dom deps + if (movedPackageJson.peerDependencies?.react) { + movedPackageJson.devDependencies.react = + movedPackageJson.peerDependencies.react; + movedPackageJson.devDependencies['react-dom'] = + movedPackageJson.peerDependencies.react; + } + + await fs.writeJson(movedPackageJsonPath, movedPackageJson, { spaces: 2 }); + } + + // Fix source code references for outdated package names + await fixSourceCodeReferences({ + packagesToBeMoved, + workspacePath, + }); + + // Create changeset for the new packages + await createChangeset({ + packages: packagesToBeMoved.map(p => + generateNewPackageName(p.packageJson.name), + ), + workspacePath, + message: + 'Migrated from [janus-idp/backstage-plugins](https://github.com/janus-idp/backstage-plugins).', + }); + + console.log(chalk.yellow`Running yarn install in new repository`); + + // Copy current yarn lock from monorepo to workspace to keep deps where possible + await fs.copyFile( + path.join(monorepoPath, 'yarn.lock'), + path.join(workspacePath, 'yarn.lock'), + ); + + await exec('yarn', ['install'], { + cwd: workspacePath, + }); + + const workspacePackageJsonPath = path.join(workspacePath, 'package.json'); + const workspacePackageJson = await fs.readJson(workspacePackageJsonPath); + + console.log(chalk.yellow`Running yarn tsc in new repository`); + try { + await exec('yarn tsc', { cwd: workspacePath, shell: true }); + } catch (error) { + console.error(`Exec failed: ${error}`); + } + + // run yarn export dynamic in each package + for (const pkg of packagesToBeMoved) { + console.log( + chalk.yellow`Running yarn export-dynamic in ${pkg.packageJson.name}`, + ); + const packagePath = path.join(workspacePath, pkg.relativeDir); + try { + await exec('yarn', ['export-dynamic', '--clean'], { + cwd: packagePath, + shell: true, + }); + } catch (error) { + console.log(error); + } + } + + console.log(chalk.yellow`Running yarn tsc:full in new repository`); + try { + await exec('yarn tsc:full', { cwd: workspacePath, shell: true }); + } catch (error) { + console.log( + chalk.blueBright`Running yarn tsc:full resulted in an error. Setting --skipLibCheck true to solve it.`, + ); + workspacePackageJson.scripts['tsc:full'] = + 'tsc --skipLibCheck true --incremental false'; + workspacePackageJson.scripts['build:api-reports'] = + 'yarn build:api-reports:only'; + await fs.writeJson(workspacePackageJsonPath, workspacePackageJson, { + spaces: 2, + }); + } + + console.log(chalk.yellow`Running yarn build:api-reports in new repository`); + try { + await exec('yarn build:api-reports', { cwd: workspacePath, shell: true }); + } catch (error) { + console.log( + chalk.blueBright`Adding --allow-all-warnings flag to build:api-reports:only`, + ); + workspacePackageJson.scripts['build:api-reports:only'] = + 'backstage-repo-tools api-reports --allow-all-warnings -o ae-wrong-input-file-type --validate-release-tags'; + await fs.writeJson(workspacePackageJsonPath, workspacePackageJson, { + spaces: 2, + }); + } + + console.log(chalk.yellow`Running yarn prettier:check in new repository`); + try { + await exec('yarn prettier:check', { cwd: workspacePath, shell: true }); + } catch (error) { + console.log( + chalk.blueBright`Running yarn prettier:check resulted in an error. Running npx prettier --write . to format files that did not pass the prettier:check`, + ); + await exec('yarn add @ianvs/prettier-plugin-sort-imports', { + cwd: workspacePath, + shell: true, + }); + await exec('npx prettier --write .', { cwd: workspacePath, shell: true }); + } + + console.log(chalk.yellow`Running npx eslint . --fix in new repository`); + try { + await exec('npx eslint . --fix', { cwd: workspacePath, shell: true }); + } catch (error) { + // Ignore the error or log a generic message + console.log( + chalk.blueBright`ESLint completed with errors, but they will be ignored.`, + ); + } + + // reset monorepo + await exec('git', ['checkout', 'main'], { cwd: monorepoPath }); + const defaultBranchName = `migrate-${new Date().getTime()}`; + + console.log( + chalk.yellow`Using ${ + branch ?? defaultBranchName + } branch in monorepo to apply changes`, + ); + if (branch) { + await exec('git', ['checkout', branch], { cwd: monorepoPath }); + } else { + await exec('git', ['checkout', '-b', defaultBranchName], { + cwd: monorepoPath, + }); + } + + // deprecate package in monorepo on new branch and remove all files except the README.md file + for (const packageToBeMoved of packagesToBeMoved) { + await deprecatePackage({ package: packageToBeMoved }); + } + + console.log( + chalk.green`Changesets created, please commit and push both repositories.`, + ); +}; From 4a5e5b025d80693be718151ed07c3f6c12bb000e Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Wed, 23 Oct 2024 13:53:30 -0400 Subject: [PATCH 2/6] update docs Signed-off-by: Kashish Mittal --- CONTRIBUTING.md | 6 +++--- README.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1e8a9ae17..1630a2c99 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -220,10 +220,10 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ yarn rhdh-cli janus-plugin migrate --monorepo-path ../backstage-plugins --workspace-name workspace-name --branch deprecate-workspace-name --maintainers @maintainer1,@maintainer2,@maintainer3 ``` -3. > [!Important] - > This script updates metadata commonly found across all plugins. Please review your migrated plugins to ensure that all references to "janus" have been updated to point to "rhdh-plugins." +3. The script will generate changesets in both repositories. Be sure to commit these changes and open pull requests. - The script will generate changesets in both repositories. Be sure to commit these changes and open pull requests. +> [!IMPORTANT] +> This script updates metadata commonly found across all plugins. Please review your migrated plugins to ensure that all references to "janus" have been updated to point to "rhdh-plugins." 4. If you run into CI issues take a look at [this github gist](https://gist.github.com/Fortune-Ndlovu/1562789f3905b4fe818b9079a3032982) which outlines the process taken to migrate argocd plugins in great detail. diff --git a/README.md b/README.md index 5adbc6d7b..ad0ad8a97 100644 --- a/README.md +++ b/README.md @@ -15,4 +15,4 @@ Contributions are welcome! To contribute a plugin, please follow the guidelines ## Plugins Workflow -The rhdh-plugins repository is organized into multiple workspaces, with each workspace containing a plugin or a set of related plugins. Each workspace operates independently, with its own release cycle and dependencies managed via npm. When a new changeset is added (each workspace has its own .changesets directory), a "Version packages ($workspace_name)" PR is automatically generated. Merging this PR triggers the release of all plugins in the workspace and updates the corresponding CHANGELOG files. +The `rhdh-plugins` repository is organized into multiple workspaces, with each workspace containing a plugin or a set of related plugins. Each workspace operates independently, with its own release cycle and dependencies managed via npm. When a new changeset is added (each workspace has its own `.changesets` directory), a "Version packages ($workspace_name)" PR is automatically generated. Merging this PR triggers the release of all plugins in the workspace and updates the corresponding `CHANGELOG` files. From 5700d9601efe4ab12d77dda2fda124a41335204a Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Wed, 23 Oct 2024 13:55:53 -0400 Subject: [PATCH 3/6] update docs Signed-off-by: Kashish Mittal --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1630a2c99..6514f34c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -223,7 +223,7 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ 3. The script will generate changesets in both repositories. Be sure to commit these changes and open pull requests. > [!IMPORTANT] -> This script updates metadata commonly found across all plugins. Please review your migrated plugins to ensure that all references to "janus" have been updated to point to "rhdh-plugins." +> This script updates metadata commonly found across all plugins. Please review your migrated plugins to ensure that all references to "janus" have been updated to point to "rhdh-plugins." 4. If you run into CI issues take a look at [this github gist](https://gist.github.com/Fortune-Ndlovu/1562789f3905b4fe818b9079a3032982) which outlines the process taken to migrate argocd plugins in great detail. From f24739e6ea85fa77d0722d26cca108ee3fe18756 Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Wed, 23 Oct 2024 13:58:12 -0400 Subject: [PATCH 4/6] update docs Signed-off-by: Kashish Mittal --- CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6514f34c5..ecd9d2a93 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,8 +19,8 @@ The `redhat-developer/rhdh-plugins` repository is designed as a collaborative sp - [Migrating a plugin](#migrating-a-plugin) - [Manual migration steps](#manual-migration-steps) - [Using the cli to migrate plugins from janus-idp/backstage-plugins](#using-the-cli-to-migrate-plugins-from-janus-idpbackstage-plugins) - - [Prerequisites](#prerequisites) - - [Steps](#steps) + - [Prerequisites](#prerequisites) + - [Steps](#steps) - [API Reports](#api-reports) - [Submitting a Pull Request](#submitting-a-pull-request) @@ -188,12 +188,12 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ ### Using the cli to migrate plugins from janus-idp/backstage-plugins -## Prerequisites +#### Prerequisites - Have a `janus-idp/backstage-plugins` fork locally cloned - Have a `backstage/rhdh-plugins` fork locally cloned -## Steps +#### Steps 1. In both repositories, create a new branch: From 83e0fca83403b9d6f289e00c4e4b35a5f9774207 Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Wed, 23 Oct 2024 14:02:34 -0400 Subject: [PATCH 5/6] update CONTRIBUTING.md Signed-off-by: Kashish Mittal --- CONTRIBUTING.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ecd9d2a93..7ecbf8f85 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,8 +19,6 @@ The `redhat-developer/rhdh-plugins` repository is designed as a collaborative sp - [Migrating a plugin](#migrating-a-plugin) - [Manual migration steps](#manual-migration-steps) - [Using the cli to migrate plugins from janus-idp/backstage-plugins](#using-the-cli-to-migrate-plugins-from-janus-idpbackstage-plugins) - - [Prerequisites](#prerequisites) - - [Steps](#steps) - [API Reports](#api-reports) - [Submitting a Pull Request](#submitting-a-pull-request) @@ -188,14 +186,9 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ ### Using the cli to migrate plugins from janus-idp/backstage-plugins -#### Prerequisites +1. Prepare your environment by cloning a fork of both the `janus-idp/backstage-plugins` and the `backstage/rhdh-plugins` repositories -- Have a `janus-idp/backstage-plugins` fork locally cloned -- Have a `backstage/rhdh-plugins` fork locally cloned - -#### Steps - -1. In both repositories, create a new branch: +2. In both repositories, create a new branch: - For `janus-idp/backstage-plugins`: @@ -208,7 +201,7 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ git checkout -b "migrate-workspace-name" ``` -2. In the `backstage/rhdh-plugins` repository, execute the janus-plugin migrate command.- Usage:`yarn rhdh-cli janus-plugin migrate --monorepo-path [path_to_backstage_plugins]--workspace-name [workspace_name] --branch [branch_name] --maintainers [maintainer1],[maintainer2],[maintainer3],...` +3. In the `backstage/rhdh-plugins` repository, execute the janus-plugin migrate command.- Usage:`yarn rhdh-cli janus-plugin migrate --monorepo-path [path_to_backstage_plugins]--workspace-name [workspace_name] --branch [branch_name] --maintainers [maintainer1],[maintainer2],[maintainer3],...` - The `path_to_backstage_plugins` is the path to the `backstage-plugins` project where the plugin(s) you want to migrate live. - The `workspace-name` is the name of the workspace you wish to create in the `rhdh-plugins` project. All plugins in the `backstage-plugins` that either are exactly or start with `@janus-idp/backstage-plugin-[workspace_name]` will be migrated to this new workspace. @@ -220,14 +213,14 @@ cp -r ../existing-plugins/plugins/plugin-name plugins/ yarn rhdh-cli janus-plugin migrate --monorepo-path ../backstage-plugins --workspace-name workspace-name --branch deprecate-workspace-name --maintainers @maintainer1,@maintainer2,@maintainer3 ``` -3. The script will generate changesets in both repositories. Be sure to commit these changes and open pull requests. +4. The script will generate changesets in both repositories. Be sure to commit these changes and open pull requests. > [!IMPORTANT] > This script updates metadata commonly found across all plugins. Please review your migrated plugins to ensure that all references to "janus" have been updated to point to "rhdh-plugins." -4. If you run into CI issues take a look at [this github gist](https://gist.github.com/Fortune-Ndlovu/1562789f3905b4fe818b9079a3032982) which outlines the process taken to migrate argocd plugins in great detail. +5. If you run into CI issues take a look at [this github gist](https://gist.github.com/Fortune-Ndlovu/1562789f3905b4fe818b9079a3032982) which outlines the process taken to migrate argocd plugins in great detail. -5. Check if the migrated plugins need to be added to janus-idp/backstage-showcase. If they do, create a wrapper for them following the steps below: +6. Check if the migrated plugins need to be added to janus-idp/backstage-showcase. If they do, create a wrapper for them following the steps below: - In `dynamic-plugins> wrappers` create a directory, name it based on your plugin (eg: `backstage-community-plugin-3scale-backend`) - Create a `src` directory within it From 81d704afab535f5d50d07d91713c249d07037809 Mon Sep 17 00:00:00 2001 From: Kashish Mittal Date: Wed, 23 Oct 2024 14:06:27 -0400 Subject: [PATCH 6/6] fix type errors Signed-off-by: Kashish Mittal --- .../packages/cli/src/commands/plugin/janus-migration.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts b/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts index dcceaa11d..0b71b3bdd 100644 --- a/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts +++ b/workspaces/repo-tools/packages/cli/src/commands/plugin/janus-migration.ts @@ -24,7 +24,6 @@ import { createWorkspace } from '../../lib/workspaces/createWorkspace'; import { ExitCodeError } from '../../lib/errors'; import { promisify } from 'util'; import { execFile } from 'child_process'; -import semver from 'semver'; const replace = require('replace-in-file'); @@ -123,12 +122,12 @@ const fixSourceCodeReferences = async (options: { for (const pkg of options.packagesToBeMoved) { const oldConfigPath = path.join( options.workspacePath, - pkg.packageJson.repository.directory, + (pkg.packageJson as any).repository?.directory, 'app-config.janus-idp.yaml', ); const newConfigPath = path.join( options.workspacePath, - pkg.packageJson.repository.directory, + (pkg.packageJson as any).repository?.directory, 'app-config.yaml', ); @@ -147,7 +146,7 @@ const fixSourceCodeReferences = async (options: { // Update janus references in catalog-info.yaml const catalogInfoPath = path.join( options.workspacePath, - pkg.packageJson.repository.directory, + (pkg.packageJson as any).repository?.directory, 'catalog-info.yaml', ); @@ -164,7 +163,7 @@ const fixSourceCodeReferences = async (options: { // Update janus references in .prettierrc.js const prettierConfigPath = path.join( options.workspacePath, - pkg.packageJson.repository.directory, + (pkg.packageJson as any).repository?.directory, '.prettierrc.js', );