diff --git a/lib/utils/check-root-user.js b/lib/utils/check-root-user.js index d917042fd..deb77678f 100644 --- a/lib/utils/check-root-user.js +++ b/lib/utils/check-root-user.js @@ -22,9 +22,19 @@ function checkRootUser(command) { if (isOneClickInstall) { // We have a Digitalocean one click installation - console.error(`${chalk.yellow('We discovered that you are using the Digitalocean One-Click install.')} + if (!isRootInstall()) { + // CASE: the user uses either the new DO image, where installations are following our setup guid (aka not-root), + // or the user followed the fix root user guide already, but the user uses root to run the command + console.error(`${chalk.yellow('Can\'t run command as \'root\' user.')} +Please use the user you set up in the installation process, or create a new user with regular account privileges and use this user to run 'ghost ${command}'. +See ${chalk.underline.green('https://docs.ghost.org/docs/install#section-create-a-new-user')} for more information\n`); + } else { + // CASE: the ghost installation folder is owned by root. The user needs to migrate the installation + // to a non-root and follow the instructions. + console.error(`${chalk.yellow('We discovered that you are using the Digitalocean One-Click install.')} You need to create a user with regular account privileges and migrate your installation to use this user. Please follow the steps here: ${chalk.underline.green('https://docs.ghost.org/docs/troubleshooting#section-fix-root-user')} to fix your setup.\n`); + } // TODO: remove this 4 versions after 1.5.0 if (includes(allowedCommands, command)) { diff --git a/test/unit/utils/check-root-user-spec.js b/test/unit/utils/check-root-user-spec.js index 658d2aa22..b7c50bdb7 100644 --- a/test/unit/utils/check-root-user-spec.js +++ b/test/unit/utils/check-root-user-spec.js @@ -44,9 +44,11 @@ describe('Unit: Utils > checkRootUser', function () { expect(exitStub.calledOnce).to.be.false; }); - it('shows special message for DigitalOcean One-Click installs', function () { + it('shows special message for DigitalOcean One-Click root installs', function () { const osStub = sandbox.stub(os, 'platform').returns('linux'); + const cwdStub = sandbox.stub(process, 'cwd').returns('/var/www/ghost'); const fsStub = sandbox.stub(fs, 'existsSync'); + const fsStatStub = sandbox.stub(fs, 'statSync').returns({uid: 0}); const processStub = sandbox.stub(process, 'getuid').returns(0); const exitStub = sandbox.stub(process, 'exit').throws(); const errorStub = sandbox.stub(console, 'error'); @@ -59,7 +61,10 @@ describe('Unit: Utils > checkRootUser', function () { throw new Error('should not be thrown'); } catch (e) { expect(e.message).to.not.equal('should not be thrown'); + expect(cwdStub.calledOnce).to.be.true; expect(fsStub.calledWithExactly('/root/.digitalocean_password')).to.be.true; + expect(fsStub.calledWithExactly('/var/www/ghost/.ghost-cli')).to.be.true; + expect(fsStatStub.calledWithExactly('/var/www/ghost/.ghost-cli')).to.be.true; expect(osStub.calledOnce).to.be.true; expect(processStub.calledOnce).to.be.true; expect(errorStub.calledOnce).to.be.true; @@ -68,9 +73,40 @@ describe('Unit: Utils > checkRootUser', function () { } }); - it('shows special message for DigitalOcean One-Click installs, but doesn\'t exit on `stop`', function () { + it('throws error command run with root for non-root installs on a Digitalocean One-Click install', function () { const osStub = sandbox.stub(os, 'platform').returns('linux'); + const cwdStub = sandbox.stub(process, 'cwd').returns('/var/www/ghost'); const fsStub = sandbox.stub(fs, 'existsSync'); + const fsStatStub = sandbox.stub(fs, 'statSync').returns({uid: 666}); + const processStub = sandbox.stub(process, 'getuid').returns(0); + const exitStub = sandbox.stub(process, 'exit').throws(); + const errorStub = sandbox.stub(console, 'error'); + + fsStub.withArgs('/root/.digitalocean_password').returns(true); + fsStub.withArgs('/var/www/ghost/.ghost-cli').returns(true); + + try { + checkRootUser('update'); + throw new Error('should not be thrown'); + } catch (e) { + expect(e.message).to.not.equal('should not be thrown'); + expect(cwdStub.calledOnce).to.be.true; + expect(fsStub.calledWithExactly('/root/.digitalocean_password')).to.be.true; + expect(fsStub.calledWithExactly('/var/www/ghost/.ghost-cli')).to.be.true; + expect(fsStatStub.calledWithExactly('/var/www/ghost/.ghost-cli')).to.be.true; + expect(osStub.calledOnce).to.be.true; + expect(processStub.calledOnce).to.be.true; + expect(errorStub.calledOnce).to.be.true; + expect(exitStub.calledOnce).to.be.true; + expect(errorStub.args[0][0]).to.match(/Can't run command as 'root' user/); + } + }); + + it('shows special message for DigitalOcean One-Click root installs, but doesn\'t exit on `stop`', function () { + const osStub = sandbox.stub(os, 'platform').returns('linux'); + const cwdStub = sandbox.stub(process, 'cwd').returns('/var/www/ghost'); + const fsStub = sandbox.stub(fs, 'existsSync'); + const fsStatStub = sandbox.stub(fs, 'statSync').returns({uid: 0}); const processStub = sandbox.stub(process, 'getuid').returns(0); const exitStub = sandbox.stub(process, 'exit').throws(); const errorStub = sandbox.stub(console, 'error'); @@ -79,7 +115,10 @@ describe('Unit: Utils > checkRootUser', function () { fsStub.withArgs('/var/www/ghost/.ghost-cli').returns(true); checkRootUser('stop'); + expect(cwdStub.calledOnce).to.be.true; expect(fsStub.calledWithExactly('/root/.digitalocean_password')).to.be.true; + expect(fsStub.calledWithExactly('/var/www/ghost/.ghost-cli')).to.be.true; + expect(fsStatStub.calledWithExactly('/var/www/ghost/.ghost-cli')).to.be.true; expect(osStub.calledOnce).to.be.true; expect(processStub.calledOnce).to.be.true; expect(errorStub.calledOnce).to.be.true;