feat(travis): remove supportPolicy (#11176)

This commit is contained in:
Rhys Arkins 2021-08-13 09:34:10 +02:00
parent edbe6aef5c
commit 123dc4444e
24 changed files with 51 additions and 510 deletions

View file

@ -2302,12 +2302,6 @@ This works because Renovate will add a "renovate/stability-days" pending status
<!-- markdownlint-enable MD001 -->
## supportPolicy
Language support is limited to those listed below:
- **Node.js** - [Read our Node.js documentation](https://docs.renovatebot.com/node#configuring-support-policy)
## suppressNotifications
Use this field to suppress various types of warnings and other notifications from Renovate.

View file

@ -17,45 +17,6 @@ Renovate can manage the Node.js version in the following files:
- The [`.nvmrc`](https://github.com/creationix/nvm#nvmrc) file for the [Node Version Manager](https://github.com/creationix/nvm)
- The [`node_js`](https://docs.travis-ci.com/user/languages/javascript-with-nodejs/#Specifying-Node.js-versions) field in [`.travis.yml`](https://docs.travis-ci.com/user/customizing-the-build/)
## How It Works
Node.js renovation in `package.json > engines` and in `.nvmrc` is enabled by default, if your existing version is pinned.
To enable `.travis.yml` renovation, you must:
1. Enable Travis renovation explicitly by setting the following Renovate configuration: `"travis": { "enabled": true }`
1. Optionally, configure a support policy (As documented below)
When Renovate processes your project's repository it will look for the files listed above and submit a single pull request that upgrades all Node.js versions simultaneously.
## Configuring Support Policy
Renovate supports a [`supportPolicy`](./configuration-options.md#supportpolicy) option that can be passed the following values and associated versions (current as of June 2021):
**Default:** `lts`
| supportPolicy | versions | description |
| ------------- | ---------- | -------------------------------------------------------- |
| all | 12, 14, 16 | All releases that have not passed their end date |
| lts | 12, 14 | All releases classified as LTS, including in maintenance |
| active | 14, 16 | All releases not in maintenance |
| lts_active | 14 | All releases both LTS and active |
| lts_latest | 14 | The latest LTS release |
| current | 16 | The latest release from "all" |
The version numbers associated with each support policy will be updated as new versions of Node.js are released, moved to LTS or maintenance, etc.
For example, to instruct Renovate to upgrade your project to the latest [Long-term Support](https://github.com/nodejs/Release#release-plan) release, you can use the following configuration:
```json
"supportPolicy": ["lts_latest"]
```
We recommend that you define this support policy inside the `node` configuration object.
This way, it is applied to all Node.js-related files.
For additional language support see the [`supportPolicy` documentation](./configuration-options.md#supportpolicy).
## Configuring which version of npm Renovate uses
When `binarySource=docker`, such as in the hosted WhiteSource Renovate App, Renovate will choose and install an `npm` version dynamically.

View file

@ -288,9 +288,6 @@ exports[`config/migration migrateConfig(config, parentConfig) migrates node to t
Object {
"node": Object {
"automerge": false,
"supportPolicy": Array [
"lts",
],
},
"travis": Object {
"enabled": true,

View file

@ -1645,15 +1645,6 @@ const options: RenovateOptions[] = [
mergeable: true,
cli: false,
},
{
name: 'supportPolicy',
description:
'Dependency support policy, e.g. used for LTS vs non-LTS etc (Node only).',
type: 'array',
subType: 'string',
stage: 'package',
allowString: true,
},
{
name: 'node',
description: 'Configuration object for Node version renovation.',

View file

@ -12,7 +12,7 @@ import type {
const defaultConfig = getConfig();
interface TestRenovateConfig extends RenovateConfig {
node?: RenovateSharedConfig & { supportPolicy?: unknown };
node?: RenovateSharedConfig;
}
describe(getName(), () => {
@ -356,7 +356,6 @@ describe(getName(), () => {
const config: TestRenovateConfig = {
node: {
enabled: true,
supportPolicy: ['lts'],
automerge: 'none' as never,
},
};
@ -372,9 +371,6 @@ describe(getName(), () => {
expect((migratedConfig.travis as RenovateSharedConfig).enabled).toBe(
true
);
expect(
(migratedConfig.node as TestRenovateConfig).supportPolicy
).toBeDefined();
});
it('migrates packageFiles', () => {
const config: TestRenovateConfig = {

View file

@ -32,6 +32,7 @@ const removedOptions = [
'groupPrBody',
'statusCheckVerify',
'lazyGrouping',
'supportPolicy',
];
// Returns a migrated config

View file

@ -91,26 +91,6 @@ describe(getName(), () => {
});
});
describe('getPackageUpdates', () => {
it('returns null', () => {
manager.getManagers().set('dummy', {
defaultConfig: {},
});
expect(manager.getPackageUpdates('unknown', null)).toBeNull();
expect(manager.getPackageUpdates('dummy', null)).toBeNull();
});
it('returns non-null', () => {
manager.getManagers().set('dummy', {
defaultConfig: {},
getPackageUpdates: () => Promise.resolve({ updates: [] }),
});
expect(manager.getPackageUpdates('dummy', {} as any)).not.toBeNull();
});
afterEach(() => {
manager.getManagers().delete('dummy');
});
});
describe('getRangeStrategy', () => {
it('returns null', () => {
manager.getManagers().set('dummy', {

View file

@ -17,8 +17,6 @@ import type {
ExtractConfig,
ManagerApi,
PackageFile,
PackageUpdateConfig,
PackageUpdateResult,
RangeConfig,
Result,
} from './types';
@ -69,17 +67,6 @@ export async function extractAllPackageFiles(
return null;
}
export function getPackageUpdates(
manager: string,
config: PackageUpdateConfig
): Result<PackageUpdateResult> | null {
if (!managers.has(manager)) {
return null;
}
const m = managers.get(manager);
return m.getPackageUpdates ? m.getPackageUpdates(config) : null;
}
export function extractPackageFile(
manager: string,
content: string,

View file

@ -4,11 +4,16 @@ exports[`manager/travis/extract extractPackageFile() returns results 1`] = `
Object {
"deps": Array [
Object {
"currentValue": Array [
6,
8,
],
"currentValue": 6,
"datasource": "github-tags",
"depName": "node",
"lookupName": "nodejs/node",
},
Object {
"currentValue": 8,
"datasource": "github-tags",
"depName": "node",
"lookupName": "nodejs/node",
},
],
}

View file

@ -1,27 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`manager/travis/package getPackageUpdates detects pinning 1`] = `
Object {
"sourceUrl": "https://github.com/nodejs/node",
"updates": Array [
Object {
"isRange": true,
"newMajor": 14,
"newValue": "10.0.1,12.3.0,null",
},
],
}
`;
exports[`manager/travis/package getPackageUpdates returns result if needing updates 1`] = `
Object {
"sourceUrl": "https://github.com/nodejs/node",
"updates": Array [
Object {
"isRange": true,
"newMajor": 14,
"newValue": "10,12,14",
},
],
}
`;

View file

@ -1,34 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`manager/travis/update updateDependency falls back to 2 spaces 1`] = `"hello: world"`;
exports[`manager/travis/update updateDependency updates values 1`] = `
"dist: trusty
language: node_js
node_js:
- '6'
- '8'
services:
- redis-server
- mongodb
- mysql
addons:
postgresql: '9.5'
before_script:
- psql -c 'create database keyv_test;' -U postgres
- mysql -u root -e 'CREATE DATABASE keyv_test;'
- mysql -u root -e 'GRANT ALL PRIVILEGES ON keyv_test.* TO 'mysql'@'localhost';'
script: npm run test:full
after_success: npm run coverage
notifications:
email:
on_success: never
"
`;
exports[`manager/travis/update updateDependency uses double quotes 1`] = `
"node_js:
- \\"6\\"
- \\"8\\"
"
`;

View file

@ -12,7 +12,7 @@ describe(getName(), () => {
it('returns results', () => {
const res = extractPackageFile('node_js:\n - 6\n - 8\n');
expect(res).toMatchSnapshot();
expect(res.deps).toHaveLength(1);
expect(res.deps).toHaveLength(2);
});
it('should handle invalid YAML', () => {
const res = extractPackageFile(invalidYAML);

View file

@ -1,5 +1,6 @@
import is from '@sindresorhus/is';
import { load } from 'js-yaml';
import * as datasourceGithubTags from '../../datasource/github-tags';
import { logger } from '../../logger';
import type { PackageDependency, PackageFile } from '../types';
@ -14,12 +15,12 @@ export function extractPackageFile(content: string): PackageFile | null {
}
let deps: PackageDependency[] = [];
if (doc && is.array(doc.node_js)) {
deps = [
{
depName: 'node',
currentValue: doc.node_js,
},
];
deps = doc.node_js.map((currentValue) => ({
depName: 'node',
datasource: datasourceGithubTags.id,
lookupName: 'nodejs/node',
currentValue,
}));
}
if (!deps.length) {
return null;

View file

@ -2,12 +2,13 @@ import { LANGUAGE_NODE } from '../../constants/languages';
import * as nodeVersioning from '../../versioning/node';
export { extractPackageFile } from './extract';
export { getPackageUpdates } from './package';
export { updateDependency } from './update';
export const language = LANGUAGE_NODE;
export const defaultConfig = {
fileMatch: ['^.travis.yml$'],
major: {
enabled: false,
},
versioning: nodeVersioning.id,
};

View file

@ -1,86 +0,0 @@
import { getName } from '../../../test/util';
import { getConfig } from '../../config/defaults';
import { getPkgReleases as _getPkgReleases } from '../../datasource';
import { getPackageUpdates } from './package';
const defaultConfig = getConfig();
const getPkgReleases: any = _getPkgReleases;
jest.mock('../../datasource');
describe(getName(), () => {
describe('getPackageUpdates', () => {
let config: any;
const RealDate = Date;
beforeAll(() => {
global.Date = class FakeDate extends RealDate {
constructor(arg?: number | string | Date) {
super(arg ?? '2020-10-28');
}
} as any;
});
afterAll(() => {
global.Date = RealDate;
});
beforeEach(() => {
config = {
...defaultConfig,
};
});
it('returns empty if missing supportPolicy', async () => {
config.currentValue = ['6', '8'];
expect(await getPackageUpdates(config)).toEqual({ updates: [] });
});
it('returns empty if invalid supportPolicy', async () => {
config.currentValue = ['6', '8'];
config.supportPolicy = ['foo'];
expect(await getPackageUpdates(config)).toEqual({ updates: [] });
});
it('returns empty if matching', async () => {
config.currentValue = ['12', '14'];
config.supportPolicy = ['lts_active'];
expect(await getPackageUpdates(config)).toEqual({ updates: [] });
});
it('returns result if needing updates', async () => {
config.currentValue = ['6', '8', '10'];
config.supportPolicy = ['lts'];
// FIXME: explicit assert condition
expect(await getPackageUpdates(config)).toMatchSnapshot();
});
it('detects pinning', async () => {
config.currentValue = ['8.4.0', '10.0.0', '12.0.0'];
config.supportPolicy = ['lts'];
getPkgReleases.mockReturnValueOnce({
releases: [
{
version: '4.4.4',
},
{
version: '5.5.5',
},
{
version: '6.11.0',
},
{
version: '7.0.0',
},
{
version: '8.9.4',
},
{
version: '9.5.0',
},
{
version: '10.0.1',
},
{
version: '12.3.0',
},
],
});
// FIXME: explicit assert condition
expect(await getPackageUpdates(config)).toMatchSnapshot();
});
});
});

View file

@ -1,62 +0,0 @@
import is from '@sindresorhus/is';
import { dequal } from 'dequal';
import { getPkgReleases } from '../../datasource';
import * as datasourceGithubTags from '../../datasource/github-tags';
import { logger } from '../../logger';
import { NodeJsPolicies, getPolicies } from '../../versioning/node/schedule';
import { getSatisfyingVersion, isVersion } from '../../versioning/semver';
import type { PackageUpdateConfig, PackageUpdateResult } from '../types';
export async function getPackageUpdates(
config: PackageUpdateConfig
): Promise<PackageUpdateResult> {
logger.trace('travis.getPackageUpdates()');
const { supportPolicy } = config;
if (!supportPolicy?.length) {
return { updates: [] };
}
const policies = getPolicies();
for (const policy of supportPolicy) {
if (!Object.keys(policies).includes(policy)) {
logger.warn({ policy }, `Unknown supportPolicy`);
return { updates: [] };
}
}
logger.debug({ supportPolicy }, `supportPolicy`);
let newValue: any[] = (supportPolicy as (keyof NodeJsPolicies)[])
.map((policy) => policies[policy])
.reduce((result, policy) => result.concat(policy), [])
.sort((a, b) => a - b);
const newMajor: number = newValue[newValue.length - 1];
if (config.rangeStrategy === 'pin' || isVersion(config.currentValue[0])) {
const versions = (
await getPkgReleases({
...config,
datasource: datasourceGithubTags.id,
depName: 'nodejs/node',
})
).releases.map((release) => release.version);
newValue = newValue
.map(String)
.map((value) => getSatisfyingVersion(versions, value));
}
if (is.string(config.currentValue[0])) {
newValue = newValue.map(String);
}
newValue.sort((a, b) => a - b);
(config.currentValue as any).sort((a, b) => a - b);
if (dequal(config.currentValue, newValue)) {
return { updates: [] };
}
return {
sourceUrl: 'https://github.com/nodejs/node',
updates: [
{
newValue: newValue.join(','),
newMajor,
isRange: true,
},
],
};
}

View file

@ -1 +1,28 @@
This manager is intended to keep Travis config files (`.travis.yml`) up-to-date. Currently it manages only the `node_js` section of files only.
This manager is intended to keep Travis config files (`.travis.yml`) up-to-date.
Currently it manages only the `node_js` section of files only.
An important limitation is that Renovate does not currently "understand" Travis's node matrix concept, so it will try to update all found node versions to the latest LTS, e.g.
```diff
node_js:
- - 8.10.0
- - 10.10.0
+ - 14.17.4
+ - 14.17.4
```
Due to this, major updates for Travis are disabled by default.
If you enable major updates and use a version matrix, then you will likely need to manually fix any major update PRs raised by Renovate.
Here's how to enable major updates in your Renovate config:
```json
{
"travis": {
"major": {
"enabled": true
}
}
}
```
If you would like to see "build matrix" support in future, please contribute ideas to [issue #11175](https://github.com/renovatebot/renovate/issues/11175).

View file

@ -1,50 +0,0 @@
import { getName, loadFixture } from '../../../test/util';
import { updateDependency } from './update';
const content = loadFixture('travis.yml');
describe(getName(), () => {
describe('updateDependency', () => {
it('updates values', () => {
const upgrade: any = {
currentValue: ['8', '6', '4'],
newValue: '6,8',
};
const res = updateDependency({ fileContent: content, upgrade });
// FIXME: explicit assert condition
expect(res).toMatchSnapshot();
});
it('falls back to 2 spaces', () => {
const upgrade: any = {
currentValue: [8, 6, 4],
newValue: '6,8',
};
const res = updateDependency({
fileContent: 'hello: world',
upgrade,
});
// FIXME: explicit assert condition
expect(res).toMatchSnapshot();
});
it('uses double quotes', () => {
const upgrade: any = {
currentValue: ['6'],
newValue: '6,8',
};
const res = updateDependency({
fileContent: 'node_js:\n - "6"\n',
upgrade,
});
// FIXME: explicit assert condition
expect(res).toMatchSnapshot();
});
it('returns null if error', () => {
const upgrade: any = {
currentValue: [8, 6, 4],
newValue: 6,
};
const res = updateDependency({ fileContent: content, upgrade });
expect(res).toBeNull();
});
});
});

View file

@ -1,31 +0,0 @@
import is from '@sindresorhus/is';
import detectIndent from 'detect-indent';
import { logger } from '../../logger';
import type { UpdateDependencyConfig } from '../types';
export function updateDependency({
fileContent,
upgrade,
}: UpdateDependencyConfig): string | null {
try {
logger.debug(`travis.updateDependency(): ${upgrade.newValue}`);
const indent = detectIndent(fileContent).indent || ' ';
let quote: string;
if (is.string(upgrade.currentValue[0])) {
quote =
fileContent.split(`'`).length > fileContent.split(`"`).length
? `'`
: `"`;
} else {
quote = '';
}
let newString = `node_js:\n`;
upgrade.newValue.split(',').forEach((version) => {
newString += `${indent}- ${quote}${version}${quote}\n`;
});
return fileContent.replace(/node_js:(\n\s+-[^\n]+)+\n/, newString);
} catch (err) {
logger.debug({ err }, 'Error setting new .travis.yml node versions');
return null;
}
}

View file

@ -50,17 +50,6 @@ export interface UpdateArtifactsConfig extends ManagerConfig {
newMajor?: number;
}
export interface PackageUpdateConfig {
currentValue?: string;
rangeStrategy?: RangeStrategy;
supportPolicy?: string[];
}
export interface PackageUpdateResult {
sourceUrl?: string;
updates: LookupUpdate[];
}
export interface RangeConfig<T = Record<string, any>> extends ManagerData<T> {
currentValue?: string;
depName?: string;
@ -253,8 +242,6 @@ export interface ManagerApi {
config?: ExtractConfig
): Result<PackageFile | null>;
getPackageUpdates?(config: PackageUpdateConfig): Result<PackageUpdateResult>;
getRangeStrategy?(config: RangeConfig): RangeStrategy;
updateArtifacts?(

View file

@ -13,49 +13,3 @@ export type NodeJsData = Record<string, NodeJsSchedule>;
export const nodeSchedule: NodeJsData = JSON.parse(
dataFiles.get('data/node-js-schedule.json')
);
export interface NodeJsPolicies {
all: number[];
lts: number[];
active: number[];
lts_active: number[];
lts_latest: number[];
current: number[];
}
export function getPolicies(): NodeJsPolicies {
const policies = {
all: [],
lts: [],
active: [],
lts_active: [],
lts_latest: [],
current: [],
};
const now = new Date();
for (const [vRelease, data] of Object.entries(nodeSchedule)) {
const isAlive = new Date(data.start) < now && new Date(data.end) > now;
if (isAlive) {
const release = parseInt(vRelease.replace(/^v/, ''), 10);
policies.all.push(release);
const isMaintenance =
data.maintenance && new Date(data.maintenance) < now;
if (!isMaintenance) {
policies.active.push(release);
}
const isLts = data.lts && new Date(data.lts) < now;
if (isLts) {
policies.lts.push(release);
if (!isMaintenance) {
policies.lts_active.push(release);
}
}
}
}
policies.current.push(policies.active[policies.active.length - 1]);
policies.lts_latest.push(policies.lts[policies.lts.length - 1]);
return policies;
}

View file

@ -17,30 +17,6 @@ Object {
"packageFile": "pom.xml",
},
],
"npm": Array [
Object {
"deps": Array [
Object {
"datasource": "npm",
"depName": "aaa",
"depType": "devDependencies",
"updates": Array [
"a",
"b",
],
},
Object {
"0": "a",
"1": "b",
"depName": "bbb",
"depType": "dependencies",
"updates": Array [],
},
],
"packageFile": "package.json",
"packageJsonType": "app",
},
],
}
`;

View file

@ -5,13 +5,10 @@ import {
mocked,
} from '../../../../test/util';
import * as datasourceMaven from '../../../datasource/maven';
import * as datasourceNpm from '../../../datasource/npm';
import * as _npm from '../../../manager/npm';
import type { ManagerApi, PackageFile } from '../../../manager/types';
import type { PackageFile } from '../../../manager/types';
import { fetchUpdates } from './fetch';
import * as lookup from './lookup';
const npm: ManagerApi = _npm;
const lookupUpdates = mocked(lookup).lookupUpdates;
jest.mock('./lookup');
@ -67,28 +64,10 @@ describe(getName(), () => {
deps: [{ datasource: datasourceMaven.id, depName: 'bbb' }],
},
],
npm: [
{
packageFile: 'package.json',
packageJsonType: 'app',
deps: [
{
datasource: datasourceNpm.id,
depName: 'aaa',
depType: 'devDependencies',
},
{ depName: 'bbb', depType: 'dependencies' },
],
},
],
};
// TODO: fix types
npm.getPackageUpdates = jest.fn((_) => ['a', 'b'] as never);
lookupUpdates.mockResolvedValue({ updates: ['a', 'b'] } as never);
await fetchUpdates(config, packageFiles);
expect(packageFiles).toMatchSnapshot();
expect(packageFiles.npm[0].deps[0].skipReason).toBeUndefined();
expect(packageFiles.npm[0].deps[0].updates).toHaveLength(2);
});
});
});

View file

@ -3,7 +3,6 @@ import { getManagerConfig, mergeChildConfig } from '../../../config';
import type { ManagerConfig, RenovateConfig } from '../../../config/types';
import { getDefaultConfig } from '../../../datasource';
import { logger } from '../../../logger';
import { getPackageUpdates } from '../../../manager';
import type { PackageDependency, PackageFile } from '../../../manager/types';
import { SkipReason } from '../../../types';
import { clone } from '../../../util/clone';
@ -38,11 +37,6 @@ async function fetchDepUpdates(
...dep,
...(await lookupUpdates(depConfig as LookupUpdateConfig)),
};
} else {
dep = {
...dep,
...(await getPackageUpdates(packageFileConfig.manager, depConfig)),
};
}
dep.updates = dep.updates || [];
}