[6.8] Add lockfile symlinks (#66212)

This commit is contained in:
Joe Portner 2020-05-13 16:13:15 -04:00 committed by GitHub
parent d038cf9934
commit d9940e6f8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 268 additions and 3 deletions

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

1
packages/kbn-es/yarn.lock Symbolic link
View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

1
packages/kbn-i18n/yarn.lock Symbolic link
View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

1
packages/kbn-pm/yarn.lock Symbolic link
View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

1
packages/kbn-test/yarn.lock Symbolic link
View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1 @@
../../yarn.lock

View file

@ -0,0 +1,21 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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.
*/
require('../src/setup_node_env');
require('../src/dev/run_check_lockfile_symlinks');

View file

@ -0,0 +1,218 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you 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 { existsSync, lstatSync, readFileSync, readlinkSync } from 'fs';
import globby from 'globby';
import { dirname } from 'path';
import { run, createFailError } from './run';
import { REPO_ROOT } from './constants';
import { File } from './file';
import { matchesAnyGlob } from './globs';
const LOCKFILE_GLOBS = ['**/yarn.lock'];
const MANIFEST_GLOBS = ['**/package.json'];
const IGNORE_FILE_GLOBS = [
// tests aren't used in production, ignore them
'**/test/**/*',
// fixtures aren't used in production, ignore them
'**/*fixtures*/**/*',
];
run(async ({ log }) => {
const paths = await globby(LOCKFILE_GLOBS.concat(MANIFEST_GLOBS), {
cwd: REPO_ROOT,
nodir: true,
gitignore: true,
ignore: [
// the gitignore: true option makes sure that we don't
// include files from node_modules in the result, but it still
// loads all of the files from node_modules before filtering
// so it's still super slow. This prevents loading the files
// and still relies on gitignore to to final ignores
'**/node_modules',
],
});
const files = paths.map(path => new File(path));
await checkLockfileSymlinks(log, files);
});
async function checkLockfileSymlinks(log, files) {
const filtered = files.filter(file => !matchesAnyGlob(file.getRelativePath(), IGNORE_FILE_GLOBS));
await checkOnlyLockfileAtProjectRoot(filtered);
await checkSuperfluousSymlinks(log, filtered);
await checkMissingSymlinks(log, filtered);
await checkIncorrectSymlinks(log, filtered);
}
async function checkOnlyLockfileAtProjectRoot(files) {
const errorPaths = [];
files
.filter(file => matchesAnyGlob(file.getRelativePath(), LOCKFILE_GLOBS))
.forEach(file => {
const path = file.getRelativePath();
const parent = dirname(path);
const stats = lstatSync(path);
if (!stats.isSymbolicLink() && parent !== '.') {
errorPaths.push(path);
}
});
if (errorPaths.length) {
throw createFailError(
`These directories MUST NOT have a 'yarn.lock' file:\n${listPaths(errorPaths)}`
);
}
}
async function checkSuperfluousSymlinks(log, files) {
const errorPaths = [];
files
.filter(file => matchesAnyGlob(file.getRelativePath(), LOCKFILE_GLOBS))
.forEach(file => {
const path = file.getRelativePath();
const parent = dirname(path);
const stats = lstatSync(path);
if (!stats.isSymbolicLink()) {
return;
}
const manifestPath = `${parent}/package.json`;
if (!existsSync(manifestPath)) {
log.warning(
`No manifest found at '${manifestPath}', but found an adjacent 'yarn.lock' symlink.`
);
errorPaths.push(path);
return;
}
try {
const manifest = readFileSync(manifestPath);
try {
const json = JSON.parse(manifest);
if (!json.dependencies || !Object.keys(json.dependencies).length) {
log.warning(
`Manifest at '${manifestPath}' has an adjacent 'yarn.lock' symlink, but manifest has no dependencies.`
);
errorPaths.push(path);
}
} catch (err) {
log.warning(
`Manifest at '${manifestPath}' has an adjacent 'yarn.lock' symlink, but could not parse manifest JSON (${err.message}).`
);
errorPaths.push(path);
}
} catch (err) {
log.warning(
`Manifest at '${manifestPath}', has an adjacent 'yarn.lock' symlink, but could not read manifest (${err.message}).`
);
errorPaths.push(path);
}
});
if (errorPaths.length) {
throw createFailError(
`These directories MUST NOT have a 'yarn.lock' symlink:\n${listPaths(errorPaths)}`
);
}
}
async function checkMissingSymlinks(log, files) {
const errorPaths = [];
files
.filter(file => matchesAnyGlob(file.getRelativePath(), MANIFEST_GLOBS))
.forEach(file => {
const path = file.getRelativePath();
const parent = dirname(path);
const lockfilePath = `${parent}/yarn.lock`;
if (existsSync(lockfilePath)) {
return;
}
try {
const manifest = readFileSync(path);
try {
const json = JSON.parse(manifest);
if (json.dependencies && Object.keys(json.dependencies).length) {
const correctSymlink = getCorrectSymlink(lockfilePath);
log.warning(
`Manifest at '${path}' has dependencies, but did not find an adjacent 'yarn.lock' symlink to '${correctSymlink}'.`
);
errorPaths.push(`${parent}/yarn.lock`);
}
} catch (err) {
log.warning(`Could not parse manifest JSON at '${path}' (${err.message}).`);
}
} catch (err) {
log.warning(`Could not read manifest at '${path}' (${err.message}).`);
}
});
if (errorPaths.length) {
throw createFailError(
`These directories MUST have a 'yarn.lock' symlink:\n${listPaths(errorPaths)}`
);
}
}
async function checkIncorrectSymlinks(log, files) {
const errorPaths = [];
files
.filter(file => matchesAnyGlob(file.getRelativePath(), LOCKFILE_GLOBS))
.forEach(file => {
const path = file.getRelativePath();
const stats = lstatSync(path);
if (!stats.isSymbolicLink()) {
return;
}
const symlink = readlinkSync(path);
const correctSymlink = getCorrectSymlink(path);
if (symlink !== correctSymlink) {
log.warning(
`Symlink at '${path}' points to '${symlink}', but it should point to '${correctSymlink}'.`
);
errorPaths.push(path);
}
});
if (errorPaths.length) {
throw createFailError(
`These symlinks do NOT point to the 'yarn.lock' file in the project root:\n${listPaths(
errorPaths
)}`
);
}
}
function getCorrectSymlink(path) {
const count = path.split('/').length - 1;
return `${'../'.repeat(count)}yarn.lock`;
}
function listPaths(paths) {
return paths.map(path => ` - ${path}`).join('\n');
}

View file

@ -102,6 +102,16 @@ module.exports = function (grunt) {
]
},
// used by the test and jenkins:unit tasks
// runs the check_lockfile_symlinks script to ensure manifests with non-dev dependencies have adjacent lockfile symlinks
checkLockfileSymlinks: {
cmd: process.execPath,
args: [
require.resolve('../../scripts/check_lockfile_symlinks'),
'--quiet', // only log errors, not warnings
],
},
// used by the test and jenkins:unit tasks
// runs the tslint script to check for Typescript linting errors
typeCheck: {

View file

@ -32,6 +32,7 @@ module.exports = function (grunt) {
'run:checkFileCasing',
'licenses',
'verifyDependencyVersions',
'run:checkLockfileSymlinks',
'run:verifyNotice',
'test:server',
'test:jest',

3
x-pack/.gitignore vendored
View file

@ -10,6 +10,3 @@
/.env
/.kibana-plugin-helpers.dev.*
!/plugins/infra/**/target
# We don't want any yarn.lock files in here
/yarn.lock

View file

@ -0,0 +1 @@
../../../yarn.lock

1
x-pack/yarn.lock Symbolic link
View file

@ -0,0 +1 @@
../yarn.lock