From d29b8e65ec64bea8d8ad07f6d32d8177f4cf242d Mon Sep 17 00:00:00 2001 From: Daniel Lockyer Date: Fri, 1 Apr 2022 14:07:04 +0100 Subject: [PATCH] Fixed symlinked directories ignored when reading packages refs https://github.com/TryGhost/Utils/commit/bd6a295674ba9fc8f187cbc4e7df3e90f77469aa - earlier this week I refactored this block of code to get rid of explicit `stat` calls, to make the fs operations a little more lightweight - I inadvertantly forgot that readdir doesn't follow symlinks, and we were previously use stat that does, so it was ignoring themes that were symlinked into `content/themes` - instead of rolling back my change, I've added an if-statement to call `fs.stat` and check the origin of the symlink to see if it's a directory - also added a test that fails without this change --- ghost/package-json/lib/package-json.js | 8 +++++- ghost/package-json/test/read.test.js | 38 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/ghost/package-json/lib/package-json.js b/ghost/package-json/lib/package-json.js index f8e1060e23..162da61839 100644 --- a/ghost/package-json/lib/package-json.js +++ b/ghost/package-json/lib/package-json.js @@ -128,11 +128,17 @@ async function readPackage(packagePath, packageName) { */ async function readPackages(packagePath) { return Bluebird.resolve(fs.readdir(packagePath, {withFileTypes: true})) - .filter(function (packageFile) { + .filter(async function (packageFile) { // Filter out things which are not packages by regex if (packageFile.name.match(notAPackageRegex)) { return; } + + if (packageFile.isSymbolicLink()) { + const packageFileOrig = await fs.stat(join(packagePath, packageFile.name)); + return packageFileOrig.isDirectory(); + } + // Check the remaining items to ensure they are a directory return packageFile.isDirectory(); }) diff --git a/ghost/package-json/test/read.test.js b/ghost/package-json/test/read.test.js index d35e394310..7d125ae310 100644 --- a/ghost/package-json/test/read.test.js +++ b/ghost/package-json/test/read.test.js @@ -99,6 +99,44 @@ describe('package-json read', function () { .catch(done) .finally(packagePath.removeCallback); }); + + it('should read directory and include symlinked directories', function (done) { + let packagePath; + let pkgJson; + + packagePath = tmp.dirSync({unsafeCleanup: true}); + pkgJson = JSON.stringify({ + name: 'test' + }); + + // create example theme + fs.mkdirSync(join(packagePath.name, 'testtheme')); + fs.writeFileSync(join(packagePath.name, 'testtheme', 'package.json'), pkgJson); + fs.writeFileSync(join(packagePath.name, 'testtheme', 'index.hbs'), ''); + + // Symlink one theme to the other so we should have 2 themes + fs.symlinkSync(join(packagePath.name, 'testtheme'), join(packagePath.name, 'testtheme2')); + + packageJSON.readPackages(packagePath.name) + .then(function (pkgs) { + pkgs.should.eql({ + testtheme: { + name: 'testtheme', + path: join(packagePath.name, 'testtheme'), + 'package.json': null + }, + testtheme2: { + name: 'testtheme2', + path: join(packagePath.name, 'testtheme2'), + 'package.json': null + } + }); + + done(); + }) + .catch(done) + .finally(packagePath.removeCallback); + }); }); describe('readPackage', function () {