feat(dashboard): show deprecated dependency warnings (#29694)

Co-authored-by: HonkingGoose <34918129+HonkingGoose@users.noreply.github.com>
This commit is contained in:
Rhys Arkins 2024-06-17 12:50:23 +02:00 committed by GitHub
parent 4b8beded1d
commit 03c034fbb5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 93 additions and 1 deletions

View file

@ -49,6 +49,23 @@ To disable the Dependency Dashboard, add the preset `:disableDependencyDashboard
This section explains some common use cases where having the Dependency Dashboard can help. This section explains some common use cases where having the Dependency Dashboard can help.
### Warnings for deprecated dependencies
If Renovate finds:
- packages flagged as deprecated on their registry, or
- packages that have a community-sourced replacement PR available
Then Renovate adds a prominent warning about these packages near the top of the Dependency Dashboard.
Here is an example of how this can look:
> The following dependencies are deprecated:
| Datasource | Name | Replacement? |
| ---------- | ------------------- | --------------------------------------------------------------------------------- |
| npm | `airbnb-prop-types` | ![Available](https://img.shields.io/badge/available-green?style=flat-square) |
| npm | `left-pad` | ![Unavailable](https://img.shields.io/badge/unavailable-orange?style=flat-square) |
### Visibility into rejected/deferred updates ### Visibility into rejected/deferred updates
Renovate's Dependency Dashboard shows an overview of all updates that are still "to do". Renovate's Dependency Dashboard shows an overview of all updates that are still "to do".

View file

@ -19,6 +19,7 @@ import {
GitHubMaxPrBodyLen, GitHubMaxPrBodyLen,
massageMarkdown, massageMarkdown,
} from '../../modules/platform/github'; } from '../../modules/platform/github';
import { clone } from '../../util/clone';
import { regEx } from '../../util/regex'; import { regEx } from '../../util/regex';
import type { BranchConfig, BranchUpgradeConfig } from '../types'; import type { BranchConfig, BranchUpgradeConfig } from '../types';
import * as dependencyDashboard from './dependency-dashboard'; import * as dependencyDashboard from './dependency-dashboard';
@ -981,6 +982,37 @@ None detected
// same with dry run // same with dry run
await dryRun(branches, platform, 0, 1); await dryRun(branches, platform, 0, 1);
}); });
it('shows deprecations', async () => {
const branches: BranchConfig[] = [];
const packageFilesWithDeprecations = clone(packageFiles);
packageFilesWithDeprecations.npm[0].deps[0].deprecationMessage =
'some deprecation message';
packageFilesWithDeprecations.npm[0].deps[2].updates.push({
updateType: 'replacement',
newName: 'prop-types-tools',
newValue: '2.17.0',
branchName: 'renovate/airbnb-prop-types-replacement',
});
PackageFiles.add('main', packageFilesWithDeprecations);
await dependencyDashboard.ensureDependencyDashboard(
config,
branches,
packageFilesWithDeprecations,
);
expect(platform.ensureIssue).toHaveBeenCalledTimes(1);
expect(platform.ensureIssue.mock.calls[0][0].body).toInclude(
'These dependencies are deprecated',
);
expect(platform.ensureIssue.mock.calls[0][0].body).toInclude(
'| npm | `cookie-parser` | ![Unavailable]',
);
expect(platform.ensureIssue.mock.calls[0][0].body).toInclude(
'npm | `express-handlebars` | ![Available]',
);
// same with dry run
await dryRun(branches, platform, 0, 1);
});
}); });
describe('multi base branch repo', () => { describe('multi base branch repo', () => {

View file

@ -224,8 +224,32 @@ export async function ensureDependencyDashboard(
return; return;
} }
logger.debug('Ensuring Dependency Dashboard'); logger.debug('Ensuring Dependency Dashboard');
// Check packageFiles for any deprecations
let hasDeprecations = false;
const deprecatedPackages: Record<string, Record<string, boolean>> = {};
logger.debug(
{ packageFiles },
'Checking packageFiles for deprecated packages',
);
for (const [manager, fileNames] of Object.entries(packageFiles)) {
for (const fileName of fileNames) {
for (const dep of fileName.deps) {
const name = dep.packageName ?? dep.depName;
const hasReplacement = !!dep.updates?.find(
(updates) => updates.updateType === 'replacement',
);
if (name && (dep.deprecationMessage ?? hasReplacement)) {
hasDeprecations = true;
deprecatedPackages[manager] ??= {};
deprecatedPackages[manager][name] ??= hasReplacement;
}
}
}
}
const hasBranches = is.nonEmptyArray(branches); const hasBranches = is.nonEmptyArray(branches);
if (config.dependencyDashboardAutoclose && !hasBranches) { if (config.dependencyDashboardAutoclose && !hasBranches && !hasDeprecations) {
if (GlobalConfig.get('dryRun')) { if (GlobalConfig.get('dryRun')) {
logger.info( logger.info(
{ title: config.dependencyDashboardTitle }, { title: config.dependencyDashboardTitle },
@ -245,6 +269,25 @@ export async function ensureDependencyDashboard(
issueBody = appendRepoProblems(config, issueBody); issueBody = appendRepoProblems(config, issueBody);
if (hasDeprecations) {
issueBody += '> ⚠ **Warning**\n> \n';
issueBody += 'These dependencies are deprecated:\n\n';
issueBody += '| Datasource | Name | Replacement PR? |\n';
issueBody += '|------------|------|--------------|\n';
for (const manager of Object.keys(deprecatedPackages).sort()) {
const deps = deprecatedPackages[manager];
for (const depName of Object.keys(deps).sort()) {
const hasReplacement = deps[depName];
issueBody += `| ${manager} | \`${depName}\` | ${
hasReplacement
? '![Available](https://img.shields.io/badge/available-green?style=flat-square)'
: '![Unavailable](https://img.shields.io/badge/unavailable-orange?style=flat-square)'
} |\n`;
}
}
issueBody += '\n';
}
const pendingApprovals = branches.filter( const pendingApprovals = branches.filter(
(branch) => branch.result === 'needs-approval', (branch) => branch.result === 'needs-approval',
); );