Skip to content

Latest commit

 

History

History
478 lines (361 loc) · 16.8 KB

File metadata and controls

478 lines (361 loc) · 16.8 KB

Migrating to Tool Kit v4

Version 4 of Tool Kit lets you configure it much more easily for your team's or project's specific use cases, without having to resort to custom plugins as often. Although we've made it as backwards compatible as possible, there are some fundamental changes to its internals and configuration that mean you'll need to update your repo's .toolkitrc.yml and any custom plugins when migrating to Tool Kit v4.

Note

For a typical repository, this migration will take ~1hr. More complex repositories (e.g. ones with custom plugins or other non-standard use cases) will take longer. If at any point you get stuck or something unexpected happens, reach out to the Platforms team and we'll be happy to help.

Tip

On Github, click the list-unordered icon on the top right to view the table of contents.

0. Update to latest versions of Tool Kit packages

Update dotcom-tool-kit and all the @dotcom-tool-kit/* packages you have installed to their latest versions. This can be done with the following command (requires jq):

npm query ":root > [name*=dotcom-tool-kit]" \
| jq '.[] | .name + "@latest"' \
| xargs npm install

If the npm install fails with an error suggesting the use of --force, edit the command to call xargs npm install --force last instead and run again. This should fix npm's version resolution and shouldn't result in any version errors.

what
  1. npm query ":root > [name*=dotcom-tool-kit]": get all packages installed as a direct dependency of your repo with dotcom-tool-kit in their name
  2. jq '(.[] | .name + "@latest")': output a list of just the name from the packages, appending @latest
  3. xargs npm install: pass those as arguments to the npm install command

So if you have dotcom-tool-kit, @dotcom-tool-kit/jest and @dotcom-tool-kit/webpack installed, this is equivalent to running:

npm install dotcom-tool-kit@latest @dotcom-tool-kit/jest@latest @dotcom-tool-kit/webpack@latest

(but without you having to manually type that out).

1. Make required changes to configuration

1.1. Rename hooks to commands

Note

Why? A Tool Kit "command" is a name for a group of tasks to run, like test:local. In previous versions of Tool Kit, hooks both defined names of commands to run, and managed configuration files. In Tool Kit v4, hooks only manage configuration files, and commands are defined in the Tool Kit configuration files, not in plugin code.

In your .toolkitrc.yml, you may have a hooks object, which specifies which Tool Kit tasks to run for commands. Rename this object to commands:

-hooks:
+commands:
   test:local:
     - Eslint
     - JestLocal
   test:ci:
     - Eslint
     - JestCI
   run:local:
     - Nodemon
     - NextRouter

1.2. Move options to options.plugins

Note

Why? In previous versions of Tool Kit, options were plugin-wide. In Tool Kit v4, options can be specified separately for plugins, tasks, and hooks. Although Tool Kit v4 can load a .toolkitrc.yml with the old options structure, some options have moved to task-specific options, and so your old options won't validate against the new schema.

In your .toolkitrc.yml, you'll have an options object. Move this object so it's nested another level deeper, under a plugins key in options:

 options:
+  plugins:
     "@dotcom-tool-kit/next-router":
       appName: article
     "@dotcom-tool-kit/eslint":
       files:
         - "**/*.{js,jsx,yaml,yml,json}"
     "@dotcom-tool-kit/doppler":
       project: next-article
     "@dotcom-tool-kit/heroku":
       pipeline: ft-next-article
       systemCode: next-article
       scaling:
         ft-next-article-eu:
           web:
             size: standard-2X
             quantity: 5

1.3. Move task-specific options to the relevant tasks

Run npx dotcom-tool-kit --help to validate your config. With this example, Tool Kit warns us that the @dotcom-tool-kit/eslint options have completely moved, and some of the @dotcom-tool-kit/heroku options have moved:

There are options in your .toolkitrc.yml that aren't what Tool Kit expected.

⚠️ 1 issue in @dotcom-tool-kit/eslint:

  • options for the @dotcom-tool-kit/eslint plugin have moved to options.tasks.Eslint

⚠️ 1 issue in @dotcom-tool-kit/heroku:

  • the option scaling has moved to options.tasks.HerokuProduction.scaling

Please update the options so that they are the expected types. You can refer to the README for the plugin for examples and descriptions of the options used.

So we need to move these options from the sections in options.plugins to a new options.tasks section:

 options:
   plugins:
     "@dotcom-tool-kit/next-router":
       appName: article
-    "@dotcom-tool-kit/eslint":
-      files:
-        - "**/*.{js,jsx,yaml,yml,json}"
     "@dotcom-tool-kit/doppler":
       project: next-article
     "@dotcom-tool-kit/heroku":
       pipeline: ft-next-article
       systemCode: next-article
-      scaling:
-        ft-next-article-eu:
-          web:
-            size: standard-2X
-            quantity: 5
+  tasks:
+    Eslint:
+      files:
+        - "**/*.{js,jsx,yaml,yml,json}"
+    HerokuProduction:
+      scaling:
+        ft-next-article-eu:
+          web:
+            size: standard-2X
+            quantity: 5

Note

As well as options that have completely moved, some options have been removed entirely. It's safe to keep these in your .toolkitrc.yml, and Tool Kit won't warn about them. Check the readmes for the plugins you're using to see which options are still required.

1.4. Update names of renamed tasks

Note

Why? In previous versions of Tool Kit, options were plugin-wide. In Tool Kit v4, we have task-specific options, and these can be overriden depending on which command is running a task. This means instead of needing to have e.g. separate JestLocal and JestCi tasks, we can have a single Jest task, with a boolean ci option that can be set to true for the test:ci command only.

Run npx dotcom-tool-kit --help again. If you have any commands in your .toolkitrc.yml that have been renamed, you'll get a message like:

These tasks don't exist, but are configured to run from commands:

  • JestLocal (assigned to test:local by your app)
  • JestCi (assigned to test:ci by your app)

They could be misspelt, or defined by a Tool Kit plugin that isn't used by this app.

In your .toolkitrc.yml, rename the tasks to their v4 equivalents, and specify the appropriate options so you get equivalent behaviour to the old task:

 commands:
   test:local:
-     - JestLocal
+     - Jest
   test:ci:
-     - JestCi
+     - Jest:
+         ci: true
Full list of renamed tasks

This is a list of all tasks that have been renamed, their v4 equivalents, and the options you need to specify for the same behaviour:

Renamed task Tool Kit v4 equivalent Options to set
BabelDevelopment Babel envName: 'development'
BabelProduction Babel envName: 'production'
CypressCi Cypress {}
CypressLocal Cypress {}
JestCi Jest ci: true
JestLocal Jest {}
TypeScriptBuild TypeScript {}
TypeScriptTest TypeScript noEmit: true
TypeScriptWatch TypeScript watch: true
WebpackDevelopment Webpack envName: 'development'
WebpackProduction Webpack envName: 'production'
WebpackWatch Webpack watch: true, envName: 'development'

2. Migrate any custom plugins

Tip

If you only use built-in Tool Kit plugins, you can skip to step 3.

Built-in plugins are listed in your .toolkitrc.yml with npm package names, like @dotcom-tool-kit/webpack; custom plugins are referenced by relative file path, like ./tool-kit/canary.

Yes, I have custom plugins

2.1. Steps for all plugins

A plugin's .toolkitrc.yml acts as a manifest to tell Tool Kit what to load. To prevent issues when attempting to load an old plugin, there is now a manifest version. A .toolkitrc.yml without a version is implicitly version 1. Tool Kit v4 will only load plugins with a version 2 manifest.

Add the version to your plugin's .toolkitrc.yml (most plugins' .toolkitrc.yml files will be empty before this):

+version: 2

2.2. Plugins with custom tasks

First, check to see if your custom task can now be provided by a built-in plugin. For example, if you're defining a custom task so you can run the same task multiple times with different options, this can now be done via command-specific task options:

commands:
  test:local:
    - Jest
  test:e2e-local:
    - Jest:
        configFile: jest.e2e.config.js

2.2.1. Import base classes from @dotcom-tool-kit/base

The @dotcom-tool-kit/types package, which previously exported the base classes for Tasks and Hooks, has been split up into multiple packages. Replace this package with @dotcom-tool-kit/base in your dependencies:

$ npm remove @dotcom-tool-kit/types
$ npm install --save-dev @dotcom-tool-kit/base

And in your plugin module:

-const { Task } = require('@dotcom-tool-kit/types')
+const { Task } = require('@dotcom-tool-kit/base')

2.2.2. Add task entrypoints to manifest

Tool Kit no longer eagerly loads your plugin's Javascript entrypoint. The plugin .toolkitrc.yml must now list the tasks your plugin defines, and you can only export one task per entrypoint.

Let's say your plugin has an index.js that exports two tasks:

const { Task } = require('@dotcom-tool-kit/base')

class CustomTaskOne extends Task {
  run() {}
}

class CustomTaskTwo extends Task {
  run() {}
}

exports.tasks = [CustomTaskOne, CustomTaskTwo]

You'll need to split this into multiple files:

custom-task-one.jscustom-task-two.js
const { Task } = require('@dotcom-tool-kit/base')

class CustomTaskOne extends Task {
  run() {}
}

module.exports = CustomTaskOne
const { Task } = require('@dotcom-tool-kit/base')

class CustomTaskTwo extends Task {
  run() {}
}

module.exports = CustomTaskTwo

You'll then need to specify these task entry points in your plugin's .toolkitrc.yml:

version: 2

tasks:
  CustomTaskOne: ./custom-task-one.js
  CustomTaskTwo: ./custom-task-two.js

The keys in the .toolkitrc.yml are what determines the task name visible to Tool Kit, which was previously determined by the actual class name, so make sure they match.

2.2.3. Custom tasks with options

Your existing custom task may have been using this.options to reference options from your .toolkitrc.yml. Now that we have separate plugin-wide and task-specific options, this.options in tasks refers to the task-specific options. You'll need to either change your task to reference this.pluginOptions to access the plugin-wide options, or move your .toolkitrc.yml options for your plugin to task-specific options. Which you choose depends on the use cases for your plugin's options:

task-specific optionsplugin-wide options
when to use options that could be different if the task could be reused for different use cases, e.g. a path to a config file options that would be the same for every task in a plugin no matter when they're being run, e.g. a system code

.toolkitrc.yml

options:
  tasks:
    CustomTask:
      config: 'path/to/config.js'

commands:
  build:local:
    - CustomTask
  build:ci:
    - CustomTask:
        config: 'path/to/ci-config.js'
options:
  plugins:
    './tool-kit/custom-plugin':
      systemCode: 'next-article'

your custom task

class CustomTask extends Task {
  run() {
    // { config: "path/to/config.js" },
    // or, if run from `build:ci`,
    // { config: "path/to/ci-config.js" }
    this.options
  }
}
class CustomTask extends Task {
  run() {
    // { systemCode: "next-article" }
    this.pluginOptions
  }
}

It's possible to mix and match task-specific and plugin-wide options, so if you have some options that fall under one use case and some under the other, you should split the options up as appropriate.

2.3. Plugins with custom "placeholder" hooks

If you're defining a "placeholder hook" so that you have a custom Tool Kit command you can run, this is no longer required; commands can be defined in your repo's .toolkitrc.yml. A placeholder hook will look something like this:

const { Hook } = require('@dotcom-tool-kit/types')

class Placeholder extends Hook {
  check() {
    return true
  }
  install() {}
}

exports.hooks = {
  'custom:command': Placeholder
}

With Tool Kit v4, you no longer need to define this command as a hook to run it; specifying commands that run tasks in your .toolkitrc.yml is all that's needed. Your placeholder hook can be deleted.

2.4. Plugins with hooks extending PackageJsonHook or CircleCIConfigHook

Instead of needing to define new package.json scripts or CircleCI jobs in plugins, you can now do this via hook configuration options in your repo's main .toolkitrc.yml.

A custom package.json hook might look like this:

const { PackageJsonHook } = require('@dotcom-tool-kit/package-json-hook')

class DemoPublishHook extends PackageJsonHook {
    constructor() {
        super(...arguments)
        this.key = 'demo-publish'
        this.hook = 'demo:publish'
    }
}

exports.hooks = { 'demo:publish': DemoPublishHook }

This manages package.json to add a scripts["demo-publish"] that calls dotcom-tool-kit demo:publish. To replicate this, add this configuration in your repo's .toolkitrc.yml:

options:
  hooks:
    - PackageJson:
        scripts:
          demo-publish: 'demo:publish'

A custom .circleci/config.yml hook might look like this:

const CircleCiConfigHook = require('@dotcom-tool-kit/circleci/lib/circleci-config');
const { TestCI } = require('@dotcom-tool-kit/circleci/lib/index');

class DeployProduction extends CircleCiConfigHook.default {
	constructor () {
		super(...arguments);
		this.job = 'tool-kit/deploy-production';
		this.jobOptions = {
			requires: [new TestCI(this.logger).job],
			filters: { branches: { only: 'main' } }
		};
	}
}

exports.hooks = {
	'deploy:production': DeployProduction
}

This adds a tool-kit/deploy-production job that depends on the tool-kit/test-ci job. To replicate this, add this configuration in your repo's .toolkitrc.yml:

options:
  hooks:
    - CircleCi:
        jobs:
          - name: deploy-production
            command: 'deploy:production'
        workflows:
          - name: 'tool-kit'
            jobs:
              - name: 'deploy-production'
                requires:
                  - 'test'
                custom:
                  filters:
                    branches:
                      only: main

2.5. Plugins with hooks doing other things

You'll need to talk to the Platforms team to get support with your migration.

3. Regenerate config files and test

Once npx dotcom-tool-kit --help is no longer reporting any configuration or plugin errors, run npx dotcom-tool-kit --install. This will regenerate your package.json and .circleci/config.yml. If you've previously opted out of having your .circleci/config.yml, the new version of Tool Kit might not be able to validate it; get in touch with the Platforms team and we'll help you update it. Note that Tool Kit v4 makes CircleCI config file generation much more configurable and flexible, so in many cases you'll no longer have to opt out of config file management.

Try running local tasks such as npm run build, npm test, and npm start. If everything's gone well, they'll run successfully; otherwise, Tool Kit should provide an error message that explains how to fix what's wrong. If you're stuck, talk to us. Commit your changes and open a pull request with them to test your CircleCI workflow is working as expected, and when you merge the PR, keep a close eye on the main branch build.