Working with the admin client

This guide is about day-to-day development of Ghost-Admin (our ember.js admin client) that can be found in your local Ghost install at core/client after following the normal Ghost developer install guide

Pre-requisites

For ember.js development we have one primary pre-requisite and that is ember-cli which should have been installed globally by the yarn run init command.

Even if you already have these installed it's worth running these commands again as it will update them to the latest version.

  1. yarn global add ember-cli - makes the ember command available
  2. yarn global add bower - another dependency manager, makes the bower command available

Watchman

ember-cli will make use of watchman if it's available, this dependency isn't strictly necessary but it does speed things up and helps ensure that file changes are picked up reliably.

Check the ember-cli watchman docs for more info and an installation guide.

Git Setup

We use git submodules to manage Ghost's sub-projects such as Ghost-Admin and Casper. The npm run init command that is run as part of the Ghost installation will set this up for you but there are a few things that make it slightly different to the typical single-project git workflow...

  1. Treat core/client as if it is a clone of the Ghost-Admin repository, it will (mostly) behave as though it's a separate git repository
  2. After the initial Ghost installation you will want to configure your core/client repo so that you can make PRs and do all that fun git stuff

    1. Ensure you have forked Ghost-Admin and you know your fork's git location (eg. git@github.com:<username>/Ghost-Admin.git)
    2. cd core/client
    3. git remote rename origin upstream
    4. git remote set-url upstream git@github.com:TryGhost/Ghost-Admin.git
    5. git remote add origin git@github.com:<username>/Ghost-Admin.git
    6. git remote --verbose should now output something like this:

       origin    git@github.com:<username>/Ghost-Admin.git (fetch)
       origin    git@github.com:<username>/Ghost-Admin.git (push)
       upstream  git@github.com:TryGhost/Ghost-Admin.git (fetch)
       upstream  git@github.com:TryGhost/Ghost-Admin.git (push)
      
  3. You can now use any git command inside core/client as normal. Pushes to origin will go to your fork from where you can open PRs
  4. Run git checkout master && git pull upstream master to switch to the master branch and update it
  5. If you ever need to edit the git config for core/client it's not where you might expect at ghost/core/client/.git/config but instead located higher up in the directory tree at ghost/.git/modules/core/client/config

Day-to-day Development

Getting up to date with master

Most of the time when you're getting started with development for the day you'll want to ensure that you're working on top of the latest client code. The typical workflow for this is to switch to the master branch and pull in changes before making sure you have the latest dependencies installed...

  1. git checkout master
  2. git pull upstream master
  3. yarn
  4. bower install

If you were working on a feature branch you'll probably want to rebase it now so that you're working on top of the latest code rather than staying at the point in time you originally created your branch:

  1. git checkout <feature-branch>
  2. git rebase master

Note: you really do want to use rebase and not merge!
yarn.lock conflicts? check the yarn.lock conflict resolution guide

Making client changes

Making changes to the client isn't too dissimilar to making changes to Ghost itself, it helps to have multiple terminal tabs open so that you can run grunt dev in one and continue to use git or other commands in the other.

In the first tab:

  1. Ensure you're in the root of your ghost directory
  2. Run grunt dev - this starts Ghost in development mode and also starts ember-cli watching the client files to rebuild any time a client file is changed
  3. Wait until you see something like Build successful - 18858ms. in the output - if it's been a while since you last ran grunt dev this might take some time, possibly 1min+ on a completely fresh install
  4. Open the admin area in a browser, this will typically be at http://localhost:2368/ghost/

You can now edit any core/client/* files in your editor of choice, any change to a file will kick off an automatic rebuild, if you're watching the grunt dev output you will see something like this:

file changed components/gh-publishmenu-draft.js

Build successful - 2468ms.

Slowest Nodes (totalTime => 5% )              | Total (avg)         
----------------------------------------------+---------------------
AssetRewrite (1)                              | 1248ms              
PostcssCompiler (2)                           | 171ms (85 ms)       
Fingerprint (1)                               | 152ms               
SourceMapConcat: Concat: Vendor /asset... (1) | 128ms

This will work for nearly every js/hbs/css/asset file, even changes to files in node_modules will be picked up which is useful when debugging addons. After the rebuild's finished you will need to manually refresh the browser to see changes but this should soon be automatic.

You can typically leave grunt dev running for as long as you are working - if you experience issues with it crashing please mention it on slack or raise an issue including the error log and anything you can remember doing when it died.

To stop the development server press <kbd>Ctrl-C</kbd> once in the tab where grunt dev is running, it will sometimes take a second or two to shut down whilst ember-cli does some cleanup in the background.

There is one file that won't rebuild and will need grunt dev to be stopped and restarted:

  • ember-cli-build.js - this is what configures the ember-cli build pipeline, it's not edited regularly but if it is you'll need to hit <kbd>Ctrl-C</kbd> in your grunt dev tab then run grunt dev again.

Managing dependencies

We have a yarn.lock file in the repository that is used on Travis for CI tests and by core team members. As such if you are adding/removing/changing dependencies you always need to use the corresponding yarn commands to ensure that the lockfile is updated and Travis/team mates will have the correct dependencies installed.

  • Adding dependencies: yarn add some-package -D (all Ghost-Admin deps will be devDependencies)
  • Removing dependencies: yarn remove some-package
    • Note that removing the package from package.json manually then running yarn install will not fully remove the dependency from yarn.lock
  • Updating dependencies: yarn add some-package -D --exact

yarn.lock conflicts

When rebasing a feature branch it's possible you'll get conflicts on yarn.lock because there were dependency changes in both master and <feature-branch>. I find the best way to resolve these is to work through the following:

  1. Note what dependencies have changed in package.json (for this example say dev dep dev-1 was added and dev dep dev-2 was removed)
  2. git reset HEAD package.json yarn.lock - unstage the files
  3. git checkout -- package.json yarn.lock - remove local changes
  4. yarn add dev-1 -D - re-adds the dependency and updates yarn.lock
  5. yarn remove dev-2 - removes the dependency and updates yarn.lock
  6. git add package.json yarn.lock - re-stage the changes
  7. git rebase --continue - continue with the rebase

That ensures that we're not introducing weird edge cases because we let yarn auto-generate the lockfile rather than trying to manually merge potentially incompatible changes.

Testing

Any time you have grunt dev running the client tests will be available at http://localhost:4200/tests

When loading the tests url all of the tests will run in your current browser and the tests will re-run any time a client file is changed.

You can click on the test group title links to run only certain tests or use the individual > links to run a single test. You can also modify the ?grep=... query param to limit tests in the same way as the ember test -f '...' command mentioned below.

Testing Tips

Opening the web inspector and following the Console tab whilst the tests are running can be useful - sometimes you will get a more useful error there than is shown in the web interface, or if you have added console.log statements they can help highlight the test behaviour you're working on.

It can be helpful to enable Mirage logs whilst testing, this will output every handled request and the return value in the web inspector console tab - this is very useful as there won't be anything in the Network tab during tests because every request is mocked and handled by ember-cli-mirage.

When working on acceptance tests it's possible to pause the tests running in the browser so that you can visually inspect the screen and use the normal web inspector Inspect element and console techniques for examining the current app state. To initiate the pause, find the point in the acceptance test that you want to stop and add the following lines:

this.timeout(0);
return pauseTest();

If you are running tests from the CLI and pass the --server argument you are free to use the web interface in the opened browsers to limit the tests which are being run in the same way as mentioned above when using http://localhost:4200/tests

CLI Commands

Client tests are always run by initiating a variation of ember test ... inside the core/client directory. The available params in order of usefulness from low to high...

  • ember test - this is what Travis runs, it will run all of the tests in Chrome/Firefox sequentially then exit. There will be a lot of log output in the console and the browsers won't stay open for you to view what tests have/haven't passed so it's not particularly useful for local development
  • ember test --server (or -s) - similar to ember test in that it will run all tests but it differs significantly in that Chrome and Firefox will run in parallel and will not quit once the tests finish but instead stay open waiting for file changes which will trigger the tests to re-run
  • ember test -f 'gh-my-component' - will only run tests where the describe() or it() text matches the supplied argument - watch out, it's case-sensitive!
  • ember test --launch=chrome - will only open Chrome rather than Chrome+Firefox, useful to keep system resources under control
  • ember test -s -f 'Acceptance: Settings - General' --launch=chrome - the typical test command to use when doing local development, it will only run the tests you're interested in to keep the test cycle fast, it will re-run on any file changes, and it only opens Chrome so you don't have two automated browsers bringing your laptop to it's knees.

Troubleshooting

ENOENT: no such file or directory, stat 'core/built/assets/img/favicon.ico' at Error (native)

Your admin client was not build yet. Please run grunt prod for production or grunt dev.

Admin not loading with TypeError: Cannot read property 'tagName' of undefined

If you are using ember test commands you will not be able to use grunt dev at the same time - ember test will generate test-specific files that will result in a blank screen and the above error message in the web inspector console when you try to access your admin area. After running ember test you will need to start grunt dev or grunt build and wait for the "Build successful" line before the admin is usable again.

Working with the admin client