Skip to content

Commit bf5e981

Browse files
authored
Add check for prereleases in installed packages (#10)
* Add check for prereleases in installed packages * Update gitignore * Add tests
1 parent 8aef4ff commit bf5e981

File tree

13 files changed

+6966
-73
lines changed

13 files changed

+6966
-73
lines changed

.eslintrc.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,10 @@ env:
66
extends: "eslint:recommended"
77
parserOptions:
88
ecmaVersion: 12
9+
overrides:
10+
# override for jest
11+
- files:
12+
- "**/*.test.js"
13+
env:
14+
jest: true
915
rules: {}

.gitignore

Lines changed: 149 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
coverage
2-
3-
# Created by https://www.toptal.com/developers/gitignore/api/macos,vim
4-
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,vim
1+
# Created by https://www.toptal.com/developers/gitignore/api/macos,vim,node
2+
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,vim,node
53

64
### macOS ###
75
# General
@@ -10,7 +8,8 @@ coverage
108
.LSOverride
119

1210
# Icon must end with two \r
13-
Icon
11+
Icon
12+
1413

1514
# Thumbnails
1615
._*
@@ -31,6 +30,150 @@ Network Trash Folder
3130
Temporary Items
3231
.apdisk
3332

33+
### macOS Patch ###
34+
# iCloud generated files
35+
*.icloud
36+
37+
### Node ###
38+
# Logs
39+
logs
40+
*.log
41+
npm-debug.log*
42+
yarn-debug.log*
43+
yarn-error.log*
44+
lerna-debug.log*
45+
.pnpm-debug.log*
46+
47+
# Diagnostic reports (https://nodejs.org/api/report.html)
48+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
49+
50+
# Runtime data
51+
pids
52+
*.pid
53+
*.seed
54+
*.pid.lock
55+
56+
# Directory for instrumented libs generated by jscoverage/JSCover
57+
lib-cov
58+
59+
# Coverage directory used by tools like istanbul
60+
coverage
61+
*.lcov
62+
63+
# nyc test coverage
64+
.nyc_output
65+
66+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
67+
.grunt
68+
69+
# Bower dependency directory (https://bower.io/)
70+
bower_components
71+
72+
# node-waf configuration
73+
.lock-wscript
74+
75+
# Compiled binary addons (https://nodejs.org/api/addons.html)
76+
build/Release
77+
78+
# Dependency directories
79+
node_modules/
80+
jspm_packages/
81+
82+
# Snowpack dependency directory (https://snowpack.dev/)
83+
web_modules/
84+
85+
# TypeScript cache
86+
*.tsbuildinfo
87+
88+
# Optional npm cache directory
89+
.npm
90+
91+
# Optional eslint cache
92+
.eslintcache
93+
94+
# Optional stylelint cache
95+
.stylelintcache
96+
97+
# Microbundle cache
98+
.rpt2_cache/
99+
.rts2_cache_cjs/
100+
.rts2_cache_es/
101+
.rts2_cache_umd/
102+
103+
# Optional REPL history
104+
.node_repl_history
105+
106+
# Output of 'npm pack'
107+
*.tgz
108+
109+
# Yarn Integrity file
110+
.yarn-integrity
111+
112+
# dotenv environment variable files
113+
.env
114+
.env.development.local
115+
.env.test.local
116+
.env.production.local
117+
.env.local
118+
119+
# parcel-bundler cache (https://parceljs.org/)
120+
.cache
121+
.parcel-cache
122+
123+
# Next.js build output
124+
.next
125+
out
126+
127+
# Nuxt.js build / generate output
128+
.nuxt
129+
dist
130+
131+
# Gatsby files
132+
.cache/
133+
# Comment in the public line in if your project uses Gatsby and not Next.js
134+
# https://nextjs.org/blog/next-9-1#public-directory-support
135+
# public
136+
137+
# vuepress build output
138+
.vuepress/dist
139+
140+
# vuepress v2.x temp and cache directory
141+
.temp
142+
143+
# Docusaurus cache and generated files
144+
.docusaurus
145+
146+
# Serverless directories
147+
.serverless/
148+
149+
# FuseBox cache
150+
.fusebox/
151+
152+
# DynamoDB Local files
153+
.dynamodb/
154+
155+
# TernJS port file
156+
.tern-port
157+
158+
# Stores VSCode versions used for testing VSCode extensions
159+
.vscode-test
160+
161+
# yarn v2
162+
.yarn/cache
163+
.yarn/unplugged
164+
.yarn/build-state.yml
165+
.yarn/install-state.gz
166+
.pnp.*
167+
168+
### Node Patch ###
169+
# Serverless Webpack directories
170+
.webpack/
171+
172+
# Optional stylelint cache
173+
174+
# SvelteKit build / generate output
175+
.svelte-kit
176+
34177
### Vim ###
35178
# Swap
36179
[._]*.s[a-v][a-z]
@@ -52,5 +195,4 @@ tags
52195
# Persistent undo
53196
[._]*.un~
54197

55-
56-
# End of https://www.toptal.com/developers/gitignore/api/macos,vim
198+
# End of https://www.toptal.com/developers/gitignore/api/macos,vim,node

index.js

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,27 @@
1-
const { readFileSync, statSync } = require("fs");
2-
3-
const expectedLockfileVersion = 2;
4-
5-
async function getPackage() {
6-
return JSON.parse(readFileSync("package.json").toString("utf-8"));
7-
}
8-
9-
async function getPackageLock() {
10-
return JSON.parse(readFileSync("package-lock.json").toString("utf-8"));
11-
}
12-
13-
async function hasYarnLock() {
14-
try {
15-
statSync("./yarn.lock");
16-
return true;
17-
} catch (e) {
18-
return false;
19-
}
20-
}
21-
22-
async function lint() {
23-
if (await hasYarnLock()) {
24-
process.stderr.write(`Unexpected yarn.lock file detected\n`);
25-
process.exit(1);
26-
}
27-
28-
process.stderr.write(
29-
`Lintint package.json for package ${(await getPackage()).name}\n`
30-
);
31-
32-
try {
33-
const { lockfileVersion } = await getPackageLock();
34-
if (lockfileVersion !== expectedLockfileVersion) {
35-
process.stderr.write(
36-
`Unexpected lockfileVersion, found “${lockfileVersion}”, expected “${expectedLockfileVersion}”\n`
37-
);
1+
const checkLockVersion = require("./lib/checks/lockVersion");
2+
const checkForPreRelease = require("./lib/checks/preReleases");
3+
const hasYarnLock = require("./lib/checks/yarn");
4+
const { FatalError } = require("./lib/errors");
5+
const { getPackageLock, getPackage } = require("./lib/utils");
6+
7+
function lint() {
8+
const pkgLock = getPackageLock();
9+
const pkgJson = getPackage();
10+
const errors = [];
11+
errors.push(hasYarnLock());
12+
errors.push(checkLockVersion(pkgLock));
13+
errors.push(checkForPreRelease(pkgJson));
14+
for (const error of errors) {
15+
if (error instanceof FatalError) {
16+
process.stderr.write(`${error.message}\n`);
3817
process.exit(1);
3918
}
40-
} catch (e) {
41-
process.stderr.write(
42-
`No package-lock.json file, ignoring lockfileVersion check\n`
43-
);
19+
if (error instanceof Error) {
20+
process.stderr.write(`${error.message}\n`);
21+
}
22+
if (error) {
23+
process.stderr.write(`${error}\n`);
24+
}
4425
}
4526
}
4627

lib/__tests__/lockVersion.test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const { FatalError } = require("../errors");
2+
const checkLockVersion = require("../checks/lockVersion");
3+
4+
describe("Check lockFile version", () => {
5+
it("should return a fatal error", () => {
6+
const result = checkLockVersion({
7+
lockfileVersion: 1,
8+
});
9+
expect(result).toBeInstanceOf(FatalError);
10+
});
11+
it("Should return a non-fatal error", () => {
12+
const result = checkLockVersion();
13+
expect(result).toBeInstanceOf(Error);
14+
expect(result).not.toBeInstanceOf(FatalError);
15+
});
16+
17+
it("Should return undefined", () => {
18+
const result = checkLockVersion({
19+
lockfileVersion: 2,
20+
});
21+
expect(result).toBeUndefined();
22+
});
23+
});

lib/__tests__/preReleases.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
const { FatalError } = require("../errors");
2+
const checkForPreRelease = require("../checks/preReleases");
3+
4+
describe("Check lockFile version", () => {
5+
it("should return a fatal error on pre-released dependency", () => {
6+
const result = checkForPreRelease({
7+
dependencies: {
8+
"@mobsuccess-devops/pre-release": "1.0.0-pr-1.0",
9+
},
10+
});
11+
expect(result).toBeInstanceOf(FatalError);
12+
});
13+
it("should return a fatal error on pre-released devDependency", () => {
14+
const result = checkForPreRelease({
15+
devDependencies: {
16+
"@mobsuccess-devops/pre-release": "1.0.0-pr-1.0",
17+
},
18+
});
19+
expect(result).toBeInstanceOf(FatalError);
20+
});
21+
it("should return a fatal error on pre-released peerDependency", () => {
22+
const result = checkForPreRelease({
23+
peerDependencies: {
24+
"@mobsuccess-devops/pre-release": "1.0.0-pr-1.0",
25+
},
26+
});
27+
expect(result).toBeInstanceOf(FatalError);
28+
});
29+
30+
it("Should return undefined", () => {
31+
const result = checkForPreRelease({
32+
dependencies: {
33+
"@mobsuccess-devops/release": "1.0.0",
34+
},
35+
devDependencies: {
36+
"@mobsuccess-devops/release": "1.0.0",
37+
},
38+
peerDependencies: {
39+
"@mobsuccess-devops/release": "1.0.0",
40+
},
41+
});
42+
expect(result).toBeUndefined();
43+
});
44+
});

lib/__tests__/yarn.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
jest.mock("fs");
2+
const { statSync } = require("fs");
3+
const hasYarnLock = require("../checks/yarn");
4+
const { FatalError } = require("../errors");
5+
6+
jest.mocked(statSync).mockImplementationOnce((path) => {
7+
if (/yarn\.lock$/.test(path)) {
8+
return { isFile: () => true };
9+
}
10+
});
11+
12+
describe("Fail if yarn.lock exists", () => {
13+
it("should fail", () => {
14+
expect(hasYarnLock()).toBeInstanceOf(FatalError);
15+
});
16+
it("Should succeed", () => {
17+
expect(hasYarnLock()).toBeUndefined();
18+
});
19+
});

lib/checks/lockVersion.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
const { FatalError } = require("../errors");
2+
3+
const expectedLockfileVersion = 2;
4+
5+
function checkLockVersion(lockFile) {
6+
if (!lockFile) {
7+
return new Error(
8+
"No package-lock.json file, ignoring lockfileVersion check"
9+
);
10+
}
11+
if (lockFile.lockfileVersion !== expectedLockfileVersion) {
12+
return new FatalError(
13+
`Unexpected lockfileVersion, found “${lockFile.lockfileVersion}”, expected “${expectedLockfileVersion}”`
14+
);
15+
}
16+
}
17+
18+
module.exports = checkLockVersion;

lib/checks/preReleases.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const { FatalError } = require("../errors");
2+
3+
function checkForPreRelease(pkgJson) {
4+
const {
5+
dependencies = {},
6+
devDependencies = {},
7+
peerDependencies = {},
8+
} = pkgJson;
9+
const deps = { ...dependencies, ...devDependencies, ...peerDependencies };
10+
for (const [name, version] of Object.entries(deps)) {
11+
if (/^@mobsuccess-devops/.test(name) && /-pr-(\d+)\.(\d+)$/.test(version)) {
12+
return new FatalError(
13+
`Unexpected pre-release dependency found: ${name}@${version}\n`
14+
);
15+
}
16+
}
17+
}
18+
19+
module.exports = checkForPreRelease;

0 commit comments

Comments
 (0)