Open more-no opened 1 year ago
@Josehower mentioned today that there were some checks that Preflight already does which sometimes catch outdated lockfiles
But there are some situations which we still do not catch
pnpm install
is the most expensive)I've reviewed the Preflight code, and it looks like we're not checking anything with the pnpm-lock.yaml
file. We do have checks for package.json
:
After some research, I found a package called package-lock-utd that checks outdated package-lock.json
files. I was able to extract the code and refactor it to check if the package.json
file and pnpm-lock.yaml
are matching. I created a script and tested it in a Portfolio project.
The script works as expected:
package.json
file and don't run pnpm install
, the script will throw the error:
package-lock.yaml is not up to date. Run "pnpm install".
package.json
file and don't run pnpm install
, the script will also throw the error:
package-lock.yaml is not up to date. Run "pnpm install".
The downside of this script is that it uses pnpm install
, which is what we want to avoid.
import { execSync } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
function existsAndIsFileSync(filePath) {
try {
const stat = fs.statSync(filePath);
return stat.isFile();
} catch (error) {
return false;
}
}
function readFileContent(filePath) {
try {
const data = fs.readFileSync(filePath, 'utf8');
return { success: true, data };
} catch (error) {
return { success: false };
}
}
const packageJsonPath = path.join(process.cwd(), 'package.json');
const pnpmLockYamlPath = path.join(process.cwd(), 'pnpm-lock.yaml');
let shouldRestorePackageLock = false;
if (!existsAndIsFileSync(packageJsonPath)) {
console.log("File 'package.json' is missing");
process.exit(1);
}
if (!existsAndIsFileSync(pnpmLockYamlPath)) {
console.log("File 'package-lock.yaml' is missing");
process.exit(1);
}
const originalPackageLockReadResult = readFileContent(pnpmLockYamlPath);
if (!originalPackageLockReadResult.success) {
console.log("Failed to read 'package-lock.yaml'");
process.exit(1);
}
try {
execSync('pnpm install');
shouldRestorePackageLock = true;
} catch {
console.log(
'Something went wrong generating the updated package-lock.yaml needed for comparison. This could be a problem with pnpm',
);
process.exit(1);
} finally {
if (shouldRestorePackageLock) {
// Restore the original package-lock.json when the process exits.
process.on('exit', () => {
fs.writeFileSync(
pnpmLockYamlPath,
originalPackageLockReadResult.data || '',
);
});
}
}
const updatedPackageLockReadResult = readFileContent(pnpmLockYamlPath);
if (!updatedPackageLockReadResult.success) {
console.log("Failed to read the updated 'package-lock.yaml'");
process.exit(1);
}
if (originalPackageLockReadResult.data !== updatedPackageLockReadResult.data) {
console.log('package-lock.yaml is not up to date. Run "pnpm install"');
process.exit(1);
}
console.log('package-lock.yaml is up to date');
Instead of using pnpm install
, maybe we can use a diff tool to compare the contents of the package.json
and pnpm-lock.yaml
files.
not sure if these pnpm install
flags help (also 250ms on my M1, maybe too slow / expensive)
--lockfile-only
flag prevents writing anything to node_modules
--frozen-lockfile
flag prevents pnpm from updating the lockfile if it needs an update and instead throws the ERR_PNPM_OUTDATED_LOCKFILE
error like it does on CI# lockfile needs no updates (exit code 0)
$ pnpm install --lockfile-only --frozen-lockfile
Done in 229ms
# lockfile needs an update (exit code 1)
$ pnpm install --lockfile-only --frozen-lockfile
ERR_PNPM_OUTDATED_LOCKFILE Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with package.json
Note that in CI environments this setting is true by default. If you still need to run install in such cases, use "pnpm install --no-frozen-lockfile"
Failure reason:
specifiers in the lockfile ({"@types/prettier":"2.7.3","eslint-config-upleveled":"5.0.4","stylelint":"15.10.3","typescript":"5.2.2"}) don't match specs in package.json ({"@babel/eslint-parser":"^7.22.15","@next/eslint-plugin-next":"^13.5.2","@types/eslint":"^8.44.2","@types/node":">=20.6.3","@types/react":"^18.2.22","@types/react-dom":"^18.2.7","@typescript-eslint/eslint-plugin":"^6.7.2","@typescript-eslint/parser":"^6.7.2","eslint":"^8.49.0","eslint-import-resolver-typescript":"^3.6.0","eslint-plugin-import":"^2.28.1","eslint-plugin-jsx-a11y":"^6.7.1","eslint-plugin-jsx-expressions":"^1.3.1","eslint-plugin-react":"^7.33.2","eslint-plugin-react-hooks":"^4.6.0","eslint-plugin-security":"^1.7.1","eslint-plugin-sonarjs":"^0.21.0","eslint-plugin-testing-library":"^6.0.1","eslint-plugin-unicorn":"^48.0.1","eslint-plugin-upleveled":"^2.1.9","typescript":"5.2.2","@types/prettier":"2.7.3","eslint-config-upleveled":"5.0.4","stylelint":"15.10.3"})
When I tried to deploy on Netlify this is what happened:
It wasn' t expecting it because Preflight didn't gave any error and it is supposed to be checking also dependency problems.
So it would be nice if in the future Preflight were able to also find the error that Netlify chatched.
BTW my problem was easily solved with:
pnpm install --no-frozen-lockfile
After that I was able to deploy.