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

allow scripts related checks. #70

Merged
merged 7 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28,178 changes: 28,178 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"execa": "^5.1.1",
"lodash": "^4.17.21",
"superstruct": "^1.0.3",
"yaml": "^2.4.1",
"yargs": "^17.7.2"
},
"devDependencies": {
Expand Down
88 changes: 76 additions & 12 deletions src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import execa from 'execa';
import path from 'path';
import { MockWritable } from 'stdio-mock';
import stripAnsi from 'strip-ansi';
import { stringify } from 'yaml';

import { main } from './main';
import { FakeOutputLogger } from '../tests/fake-output-logger';
Expand Down Expand Up @@ -61,10 +62,6 @@ describe('main', () => {
await ensureDirectoryStructureExists(
path.join(repository.directoryPath, 'src'),
);
await writeFile(
path.join(repository.directoryPath, '.yarnrc.yml'),
'',
);
await writeFile(
path.join(
repository.directoryPath,
Expand Down Expand Up @@ -96,6 +93,8 @@ describe('main', () => {
typescript: '1.0.0',
typedoc: '1.0.0',
'@metamask/auto-changelog': '1.0.0',
'@lavamoat/allow-scripts': '1.0.0',
'@lavamoat/preinstall-always-fail': '1.0.0',
},
scripts: {
test: 'test script',
Expand All @@ -109,6 +108,12 @@ describe('main', () => {
repository: {
url: 'https://github.com/MetaMask/module-lint.git',
},
lavamoat: {
allowScripts: {
'tsup>esbuild': true,
'@lavamoat/preinstall-always-fail': false,
},
},
}),
);
await writeFile(
Expand Down Expand Up @@ -155,6 +160,25 @@ describe('main', () => {
path.join(repository.directoryPath, '.gitignore'),
'content for .gitignore',
);
await writeFile(
path.join(repository.directoryPath, '.yarnrc.yml'),
stringify({
enableScripts: false,
plugins: [
{
path: '.yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs',
spec: 'https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js',
},
],
}),
);
await writeFile(
path.join(
repository.directoryPath,
'.yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs',
),
'test scripts',
);
}
const outputLogger = new FakeOutputLogger();

Expand Down Expand Up @@ -193,11 +217,15 @@ repo-1
- Do the typedoc-related \`scripts\` in \`package.json\` conform? ✅
- Do the changelog-related \`devDependencies\` in \`package.json\` conform? ✅
- Do the changelog-related \`scripts\` in \`package.json\` conform? ✅
- Do the lavamoat-related \`devDependencies\` in \`package.json\` conform? ✅
- Are postinstall scripts disabled for \`@lavamoat/preinstall-always-fail\`? ✅
- Is \`README.md\` present? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does the README conform by recommending node install from nodejs.org? ✅
- Are all of the files for Yarn Modern present, and do they conform? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does allow scripts conforms to yarn? ✅
- Is the allow-scripts Yarn plugin installed? ✅
- Does the \`src/\` directory exist? ✅
- Is \`.nvmrc\` present, and does it conform? ✅
- Is \`jest.config.js\` present, and does it conform? ✅
Expand All @@ -211,7 +239,7 @@ repo-1
- Is \`.gitattributes\` present, and does it conform? ✅
- Is \`.gitignore\` present, and does it conform? ✅

Results: 36 passed, 0 failed, 36 total
Results: 40 passed, 0 failed, 40 total
Elapsed time: 0 ms


Expand All @@ -237,11 +265,15 @@ repo-2
- Do the typedoc-related \`scripts\` in \`package.json\` conform? ✅
- Do the changelog-related \`devDependencies\` in \`package.json\` conform? ✅
- Do the changelog-related \`scripts\` in \`package.json\` conform? ✅
- Do the lavamoat-related \`devDependencies\` in \`package.json\` conform? ✅
- Are postinstall scripts disabled for \`@lavamoat/preinstall-always-fail\`? ✅
- Is \`README.md\` present? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does the README conform by recommending node install from nodejs.org? ✅
- Are all of the files for Yarn Modern present, and do they conform? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does allow scripts conforms to yarn? ✅
- Is the allow-scripts Yarn plugin installed? ✅
- Does the \`src/\` directory exist? ✅
- Is \`.nvmrc\` present, and does it conform? ✅
- Is \`jest.config.js\` present, and does it conform? ✅
Expand All @@ -255,7 +287,7 @@ repo-2
- Is \`.gitattributes\` present, and does it conform? ✅
- Is \`.gitignore\` present, and does it conform? ✅

Results: 36 passed, 0 failed, 36 total
Results: 40 passed, 0 failed, 40 total
Elapsed time: 0 ms

`,
Expand Down Expand Up @@ -486,10 +518,6 @@ Elapsed time: 0 ms
await ensureDirectoryStructureExists(
path.join(repository.directoryPath, 'src'),
);
await writeFile(
path.join(repository.directoryPath, '.yarnrc.yml'),
'',
);
await writeFile(
path.join(
repository.directoryPath,
Expand Down Expand Up @@ -521,6 +549,8 @@ Elapsed time: 0 ms
typescript: '1.0.0',
typedoc: '1.0.0',
'@metamask/auto-changelog': '1.0.0',
'@lavamoat/allow-scripts': '1.0.0',
'@lavamoat/preinstall-always-fail': '1.0.0',
},
scripts: {
test: 'test script',
Expand All @@ -534,6 +564,12 @@ Elapsed time: 0 ms
repository: {
url: 'https://github.com/MetaMask/module-lint.git',
},
lavamoat: {
allowScripts: {
'tsup>esbuild': true,
'@lavamoat/preinstall-always-fail': false,
},
},
}),
);
await writeFile(
Expand Down Expand Up @@ -580,6 +616,26 @@ Elapsed time: 0 ms
path.join(repository.directoryPath, '.gitignore'),
'content for .gitignore',
);
await writeFile(
path.join(repository.directoryPath, '.yarnrc.yml'),
stringify({
enableScripts: false,
plugins: [
{
path: '.yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs',
spec: 'https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js',
},
],
}),
);

await writeFile(
path.join(
repository.directoryPath,
'.yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs',
),
'test scripts',
);
}
const outputLogger = new FakeOutputLogger();

Expand Down Expand Up @@ -618,11 +674,15 @@ repo-1
- Do the typedoc-related \`scripts\` in \`package.json\` conform? ✅
- Do the changelog-related \`devDependencies\` in \`package.json\` conform? ✅
- Do the changelog-related \`scripts\` in \`package.json\` conform? ✅
- Do the lavamoat-related \`devDependencies\` in \`package.json\` conform? ✅
- Are postinstall scripts disabled for \`@lavamoat/preinstall-always-fail\`? ✅
- Is \`README.md\` present? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does the README conform by recommending node install from nodejs.org? ✅
- Are all of the files for Yarn Modern present, and do they conform? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does allow scripts conforms to yarn? ✅
- Is the allow-scripts Yarn plugin installed? ✅
- Does the \`src/\` directory exist? ✅
- Is \`.nvmrc\` present, and does it conform? ✅
- Is \`jest.config.js\` present, and does it conform? ✅
Expand All @@ -636,7 +696,7 @@ repo-1
- Is \`.gitattributes\` present, and does it conform? ✅
- Is \`.gitignore\` present, and does it conform? ✅

Results: 36 passed, 0 failed, 36 total
Results: 40 passed, 0 failed, 40 total
Elapsed time: 0 ms


Expand All @@ -662,11 +722,15 @@ repo-2
- Do the typedoc-related \`scripts\` in \`package.json\` conform? ✅
- Do the changelog-related \`devDependencies\` in \`package.json\` conform? ✅
- Do the changelog-related \`scripts\` in \`package.json\` conform? ✅
- Do the lavamoat-related \`devDependencies\` in \`package.json\` conform? ✅
- Are postinstall scripts disabled for \`@lavamoat/preinstall-always-fail\`? ✅
- Is \`README.md\` present? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does the README conform by recommending node install from nodejs.org? ✅
- Are all of the files for Yarn Modern present, and do they conform? ✅
- Does the README conform by recommending the correct Yarn version to install? ✅
- Does allow scripts conforms to yarn? ✅
- Is the allow-scripts Yarn plugin installed? ✅
- Does the \`src/\` directory exist? ✅
- Is \`.nvmrc\` present, and does it conform? ✅
- Is \`jest.config.js\` present, and does it conform? ✅
Expand All @@ -680,7 +744,7 @@ repo-2
- Is \`.gitattributes\` present, and does it conform? ✅
- Is \`.gitignore\` present, and does it conform? ✅

Results: 36 passed, 0 failed, 36 total
Results: 40 passed, 0 failed, 40 total
Elapsed time: 0 ms

`,
Expand Down
52 changes: 52 additions & 0 deletions src/repository-filesystem.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import fs from 'fs';
import { mock } from 'jest-mock-extended';
import path from 'path';
import { integer, object, string } from 'superstruct';
import { stringify } from 'yaml';

import { RepositoryFilesystem } from './repository-filesystem';
import { withinSandbox } from '../tests/helpers';
Expand Down Expand Up @@ -201,6 +202,57 @@ describe('RepositoryFilesystem', () => {
});
});

describe('readYamlFileAs', () => {
describe('if the file has not already been read', () => {
it('returns the content of the file as a JSON value if it conforms to the given Superstruct schema', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
await writeFile(
path.join(sandboxDirectoryPath, 'somefile.yml'),
stringify({ name: 'utils', numberOfStars: 294 }),
);
const Repo = object({
name: string(),
numberOfStars: integer(),
});
const repositoryFilesystem = new RepositoryFilesystem(
sandboxDirectoryPath,
);

const person = await repositoryFilesystem.readYamlFileAs(
'somefile.yml',
Repo,
);

expect(person).toStrictEqual({ name: 'utils', numberOfStars: 294 });
});
});

it('throws a descriptive error if the content of the file does not conform to the given Superstruct schema', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
await writeFile(
path.join(sandboxDirectoryPath, 'somefile.yml'),
stringify({ numberOfStars: 'whatever' }),
);
const Repo = object({
name: string(),
numberOfStars: integer(),
});
const repositoryFilesystem = new RepositoryFilesystem(
sandboxDirectoryPath,
);

await expect(
repositoryFilesystem.readYamlFileAs('somefile.yml', Repo),
).rejects.toThrow(
new Error(
'Missing `name`; Invalid `numberOfStars` (Expected an integer, but received: "whatever").',
),
);
});
});
});
});

describe('readDirectoryRecursively', () => {
it('reads the directory and all of its child directories, returning a flat list of files and their paths', async () => {
await withinSandbox(async ({ directoryPath: sandboxDirectoryPath }) => {
Expand Down
20 changes: 20 additions & 0 deletions src/repository-filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type fs from 'fs';
import path from 'path';
import type { Struct } from 'superstruct';
import type { ObjectSchema } from 'superstruct/dist/utils';
import { parse as yamlParse } from 'yaml';

import type { DirectoryEntry } from './misc-utils';
import {
Expand Down Expand Up @@ -85,6 +86,25 @@ export class RepositoryFilesystem {
return content;
}

/**
* Reads a YAML file within the repository, ensuring that it matches the
* given Superstruct struct.
*
* @param filePath - The path to the file relative to the repository root.
* @param struct - The Superstruct object struct that you want to match
* against the content of the file.
* @returns The contents of the file as a JSON object.
*/
async readYamlFileAs<Value extends Json, Schema extends ObjectSchema>(
filePath: string,
struct: Struct<Value, Schema>,
): Promise<Value> {
const content = await this.readFile(filePath);
const parsedYaml = yamlParse(content);
assertJsonMatchesStruct(parsedYaml, struct);
return parsedYaml;
}

/**
* Reads a directory recursively within the repository.
*
Expand Down
8 changes: 8 additions & 0 deletions src/rules/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import allYarnModernFilesConform from './all-yarn-modern-files-conform';
import classicYarnConfigFileAbsent from './classic-yarn-config-file-absent';
import packageAllowScriptsYarnConform from './package-allow-scripts-yarn-conform';
import packageAllowScriptsYarnPluginsConform from './package-allow-scripts-yarn-plugins-conform';
import packageChangelogDevDependenciesConform from './package-changelog-dev-dependencies-conform';
import packageChangelogScriptsConform from './package-changelog-scripts-conform';
import packageEnginesNodeFieldConforms from './package-engines-node-field-conforms';
import packageExportsFieldConforms from './package-exports-field-conforms';
import packageFilesFieldConforms from './package-files-field-conforms';
import packageJestDependenciesConform from './package-jest-dependencies-conform';
import packageLavamoatAllowScriptsConforms from './package-lavamoat-allow-scripts-conforms';
import packageLavamoatDevDependenciesConform from './package-lavamoat-dev-dependencies-conform';
import packageLavamoatTsupConforms from './package-lavamoat-tsup-conforms';
import packageLintDependenciesConform from './package-lint-dependencies-conform';
import packageMainFieldConforms from './package-main-field-conforms';
Expand Down Expand Up @@ -70,4 +74,8 @@ export const rules = [
requireEditorconfig,
requireGitattributes,
requireGitignore,
packageLavamoatDevDependenciesConform,
packageLavamoatAllowScriptsConforms,
packageAllowScriptsYarnConform,
packageAllowScriptsYarnPluginsConform,
] as const;
Loading
Loading