With Grunt, you run tasks, which need to be configured. You can also define a task list, in which the defined tasks will run after each other. Running task lists allows you to create workflows, in which the person that runs the workflow doesn't have to know which steps are taken to complete the workflow. It also allows you to create a single command to perform an extensive list of steps that need to run in a specific order.
We use Grunt to perform the following tasks (and more):
- Update the version of a WordPress plugin before a release
- Retrieving the latest translation files from our GlotPress installation (https://translate.yoast.com)
- Creating a zip-file containing the files that need to be published to the end-user
- Publishing a new version of a plugin to WordPress.org
Company-wide default configuration
We maintain more than a handful of WordPress plugin and as some of these plugins don't require regular updates, the tasks in these plugins are bound to get outdated. Or, they could be replaced by better tasks. In the plugins that we update more regularly (like Yoast SEO) we have replaced some of the tasks by alternative ones that are more efficient, more secure, or combine separate tasks into one.
This led us to create a separate repository that holds all default tasks and configurations.
This repository is published on NPM under @yoast/grunt-plugin-tasks, introducing one dependency that needs to be kept up-to-date. For most projects, this removes all Grunt-tasks configurations from the tracked files.
Override default configuration
Using yarn or npm to install this package registers all the tasks and configurations present in the package to the current project. If you need to deviate from the default configuration, you can create a configuration file that has the same name as the task, which allows you to override the configuration as needed.
With only the exceptions being present in the project, you can very quickly get an overview of how the workflow of that project differs from our default project.
Keeping everything up-to-date
Having a single source that controls which versions of the tasks are used, we only have to update these packages in one place, then bump the version of our own grunt-plugin-tasks package. When the new version grunt-plugin-tasks package is used in a project, all dependencies will be updated. Updating tasks only happens whenever a plugin needs a newer version of a task, or when we decide to add a new task to the default list of tasks. So there will always be at least one plugin that has the latest version of the grunt-plugin-tasks library.
The plugin-grunt-tasks repository has a package.json with all the default dependencies. When the
plugin-grunt-tasks-package is added to a project, that project will inherit all these dependencies as its own.
Whenever you need to have a specific version of a dependency in the project, configuring that version in the
package.json of your project will result in that specific version being used.
plugin-grunt-tasks package contains two folders which are relevant for the project it is added to:
config folder holds the default configurations for the tasks that have been defined in the package. Per task that is configured as a dependency in the
package.json, a configuration file should exist. When creating this package, we removed the configurations from the projects that were similar to the ones here. We have also updated some projects to be able to use this default configuration.
tasks folder holds the custom tasks we’ve created. We have created two tasks that allow us to update the version numbers in our projects. This is not mandatory for projects you undertake, so if you were to create your own dependency package, this folder would probably not have to be there.
The plugin-grunt-tasks repository has a
Gruntfile.js, dev-dependencies and a
test-folder. These are added because we have our own custom tasks and we want to have unit-tests to ensure the best quality. The project that this package is added to does not use these files. If you are creating a similar package without custom tasks, you will not need these files and folder.
If you have multiple projects that have similar needs, using shared configuration helps to reduce complexity and gives a lot of control when updating dependencies.
Allowing for specific overrides of the default configuration gives the freedom to use most of the defaults and still serve the custom needs. Have a clear overview of what is custom, because only those configurations exist in the specific project.