Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dashboard): show deprecated dependency warnings #29694

Merged
merged 11 commits into from
Jun 17, 2024
17 changes: 17 additions & 0 deletions docs/usage/key-concepts/dashboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

### 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) |
rarkins marked this conversation as resolved.
Show resolved Hide resolved
| npm | `left-pad` | ![Unavailable](https://img.shields.io/badge/unavailable-orange?style=flat-square) |
rarkins marked this conversation as resolved.
Show resolved Hide resolved

### Visibility into rejected/deferred updates

Renovate's Dependency Dashboard shows an overview of all updates that are still "to do".
Expand Down
32 changes: 32 additions & 0 deletions lib/workers/repository/dependency-dashboard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
GitHubMaxPrBodyLen,
massageMarkdown,
} from '../../modules/platform/github';
import { clone } from '../../util/clone';
import { regEx } from '../../util/regex';
import type { BranchConfig, BranchUpgradeConfig } from '../types';
import * as dependencyDashboard from './dependency-dashboard';
Expand Down Expand Up @@ -981,6 +982,37 @@ None detected
// same with dry run
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', () => {
Expand Down
45 changes: 44 additions & 1 deletion lib/workers/repository/dependency-dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,8 +224,32 @@ export async function ensureDependencyDashboard(
return;
}
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);
if (config.dependencyDashboardAutoclose && !hasBranches) {
if (config.dependencyDashboardAutoclose && !hasBranches && !hasDeprecations) {
if (GlobalConfig.get('dryRun')) {
logger.info(
{ title: config.dependencyDashboardTitle },
Expand All @@ -245,6 +269,25 @@ export async function ensureDependencyDashboard(

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)'
viceice marked this conversation as resolved.
Show resolved Hide resolved
} |\n`;
}
}
issueBody += '\n';
}

const pendingApprovals = branches.filter(
(branch) => branch.result === 'needs-approval',
);
Expand Down