Migration Guide

This document is meant to guide Girder plugin developers in transitioning between major versions of Girder. Major version bumps contain breaking changes to the Girder core library, which are enumerated in this guide along with instructions on how to update your plugin code to work in the newer version.

3.x → 5.x

Major version 5 contains significant breaking changes on many fronts. The central theme of these changes is to bring Girder into compliance with the principles of the 12-factor app to enhance its portability to various managed runtimes, and in turn its ease of scalability. We chose to skip major version 4 due to a name collision with Kitware’s Django-based stack, which we had originally called Girder 4, but now refer to as Resonant to distinguish it from this software.

The following are the breaking (or otherwise major) changes, ranked roughly in order of how disruptive they are to downstream plugins:

Front-end build system changes

The largest change to Girder in version 5 is a complete overhaul of the front-end build system. Most notably for users and plugin developers, the girder build command no longer exists, and Girder itself will no longer manage the building of its plugins’ web client bundles. Rather, each plugin is responsible for building its own web client plugin code and exposing it via the following mechanism:

from pathlib import Path

from girder.plugin import GirderPlugin, registerPluginStaticContent

class Foo(GirderPlugin):
    DISPLAY_NAME = 'Foo'

    def load(self, info):
        registerPluginStaticContent(
            plugin='foo',
            css=['/style.css'],
            js=['/girder-plugin-foo.umd.cjs'],
            staticDir=Path(__file__).parent / 'web_client' / 'dist',
            tree=info['serverRoot'],
        )

Whatever files are passed into the js and css lists will be included in the core web client after it loads. The staticDir argument should point to the directory containing the built web client code, i.e. the filenames passed into the js and css lists are relative to this local path. The tree argument should be the serverRoot object passed into the load method.

Changes within front-end plugin code

There is a mechanical conversion that will need to be performed on all front-end plugin code when moving to Girder 5: rather than static import of symbols from @girder/core in your JavaScript/TypeScript code, you will instead rely on the presence of the girder symbol in the global scope (window.girder) at runtime. Each import from @girder/core should be changed as in the following examples:

Before:

import router from '@girder/core/router';
import events from '@girder/core/events';
import { exposePluginConfig } from '@girder/core/utilities/PluginUtils';

import FrontPageView from '@girder/core/views/body/FrontPageView';
import { renderMarkdown } from '@girder/core/misc';
import { restRequest, getApiRoot } from '@girder/core/rest';
import { wrap } from '@girder/core/utilities/PluginUtils';

After:

const router = girder.router;
const events = girder.events;
const { exposePluginConfig } = girder.utilities.PluginUtils;

const FrontPageView = girder.views.body.FrontPageView;
const { renderMarkdown } = girder.misc;
const { restRequest, getApiRoot } = girder.rest;
const { wrap } = girder.utilities.PluginUtils;

Static tooling

If you are using vite as your plugin build tool, you can add the following to your vite-env.d.ts file to make TypeScript aware of the girder symbol, which enables things like IDE autocompletion and jump-to-definition for symbols under the girder namespace.

/// <reference types="vite/client" />
import { type Girder } from '@girder/core';

declare global {
  const girder: Girder;
}

Testing

Plugins are now also responsible for testing their own web client code. If your plugin was relying on any of the old testing infrastructure, those tests will no longer work. We may publish our Playwright-based front-end testing utilities as a separate package in the future, but as of 5.0, it is not exposed to downstreams.

Removal of the events daemon

The girder.events.daemon symbol has been removed, as the use of a background thread violated the WSGI contract and tightly coupled Girder to a multi-threaded server model. The main impact of this change is that any downstream users listening to the core "data.process" event, which used to be run on the background thread, should now convert their event handlers to be celery tasks or otherwise asynchronous methods if there’s any risk of the handler taking more than 1-2 seconds to complete.

New deployment requirement: local worker

With the removal of the events daemon, tasks that can take longer than 1-2 seconds to complete should now be run as celery tasks. Girder core operations that previously used the events daemon have been converted to celery tasks, and ones that require direct access to the database or the local filesystem now get sent to a celery queue called local. In order to have these tasks run, deployments must now run an additional process to server the local queue. The command to run the local worker is:

celery -A girder_worker.app worker -Q local

Event handlers for local-queue work

Many operations that previously ran synchronously in the Girder web process are now enqueued as Celery tasks on the local queue. For example, the assetstore import handler calls importDataTask.delay(...) rather than Girder 3.x’s Assetstore().importData(...) in the request thread. Model events such as s3_assetstore_imported, filesystem_assetstore_imported, and assetstore_import.after are still triggered, but they fire in whichever process runs the import—typically a Celery worker, not the web server.

events.bind calls made in a GirderPlugin load method only apply to the Girder web process. Many events still fire there as before, especially REST events (rest.*.before, rest.*.after, and so on). The worker does not load server plugins instead it discovers task modules through the girder_worker_plugins entry point (see girder_worker.GirderWorkerPluginABC). If your handlers must run when code executes on the local queue (for example during importDataTask), register them in a worker plugin subclass instead of only in load, binding them in __init__ when the worker starts. For example, the Import Tracker plugin binds assetstore_import.before, assetstore_import.after, and assetstore_import.error in ImportTrackerWorkerPlugin.__init__ (see plugins/import_tracker/girder_import_tracker/girder_worker_plugin.py).

Below is an example of a worker plugin that registers event handlers for the local queue.

from girder import events
from girder_worker import GirderWorkerPluginABC

from my_plugin.handlers import on_s3_imported, on_import_finished


class MyPluginWorker(GirderWorkerPluginABC):
    def __init__(self, app, *args, **kwargs):
        events.bind(
            's3_assetstore_imported',
            'my_plugin_s3_imported',
            on_s3_imported,
        )
        events.bind(
            'assetstore_import.after',
            'my_plugin_import_after',
            on_import_finished,
        )

    def task_imports(self):
        return ['my_plugin.tasks']

Overhaul of notifications system switch from WSGI to ASGI

In prior versions of Girder, notifications were a model in the Mongo database and were sent to the client by querying that mongo collection for documents using a timestamp threshold. Clients would use simple polling or long-polling using Server-Sent Events (SSE), but this was problematic under WSGI because those long polling threads exhausted the server’s thread pool with low utilization.

In Girder 5, we’ve resolved this by moving from a WSGI app to an ASGI app, and using proper async behavior for the notification system. Internally, this is handled using redis pub/sub, and clients now use websockets to subscribe to notifications. On the web client side, this should not require any changes, assuming your client code was using the girder events module to handle async notifications. If you were using a custom client with the /notification/stream API endpoint, you will need do update your client to subscribe to the websocket channel instead, which is at the path /notifications/me?token=<girder_auth_token>.

One behavioral change to note is that events are no longer queried based on a timestamp threshold, but rather are sent to the client as they are received in pub/sub style. This means that the client will only receive these events if they are listening on the channel. Under the previous implementation, it was possible for clients to receive the same event multiple times, but that should no longer be the case – clients should receive any given notification at most once.

On the server side, if you were using the Notification Mongo model, you will need to change your code to use the new API, as that model has been removed entirely. Instead, use the new girder.notification module to send notifications to the client. Example code change:

Before:

from girder.models.notification import Notification

Notification().createNotification(
    type='my_notification_type', data={...}, user=user,
    expires=now(timezone.utc) + timedelta(seconds=30)
)

After:

from girder.notification import Notification

Notification(type='my_notification_type', data={...}, user=user).flush()

Note that the notification won’t actually be sent on the channel until you call flush() on it. For progress notifications, please use the existing ProgressContext class, which has the same API as before. It has been internally updated to use the new async notification system.

Removal of setResponseTimeLimit function

The girder.utility.progress.setResponseTimeLimit function was removed because it violated the WSGI contract, and any operation that can take more than a few seconds to complete should be converted to a celery task that runs on the local queue or an alternative queue.

S3 assetstore adapter API protocol change

Previously, clients wanting to upload directly to an S3 assetstore using presigned URLs were responsible for performing the multipart finalization step for uploads that were too large to be sent in a single chunk. In Girder 5, the finalization step is now always performed by the server. Clients will no longer receive a presigned URL to perform the step, and they are no longer responsible for maintaining their own parts list. Uploads to S3 proxied through Girder will not require changes, and Girder’s web client has already been updated to accommodate this change.

Dynamic route configuration system removed

The server-side route table infrastructure that allowed dynamically updating URL paths from which webroots would be served has been removed. URL routing should be known at startup time, and not changed dynamically. This means that the girder.plugin.registerPluginWebroot function has been removed.

The main challenge this presents is specifically for use cases where downstreams need to serve the core Girder front-end application from a base path other than the server root (/), since with the 5.0 front-end build changes, the front-end application is bundled in and built with a static base of /. For this specific use case, one strategy that’s supported is to build the core front-end application with a different base path, and then configure your server to serve the modified front-end client from the desired path on the filesystem.

Example

In your plugin initialization function, add the following:

core_girder = info['serverRoot'].apps['']
core_girder.script_name = '/new_root'
info['serverRoot'].mount(core_girder, '/new_root', core_girder.config)
del info['serverRoot'].apps['']
# Mount your own app under `info['serverRoot']` if you want

Then, build the core front-end application with the desired base path:

git clone git@github.com:girder/girder.git
cd girder/girder/web
npx vite build --base=/new_root/
export GIRDER_STATIC_ROOT_DIR=$(pwd)/dist

With that variable set in your environment, the Girder core web client will now be served under /new_root/ rather than /.

Note

Remapping apps[''] only moves the static SPA. The REST API (including Swagger at /api/v1) stays on its original /api mount unless you duplicate it. After the remount above, and before del info[‘serverRoot’].apps[‘’], add:

api_cp_app = info['serverRoot'].apps['/api']
info['serverRoot'].mount(api_cp_app.root, '/new_root/api', api_cp_app.config)

Logging changes

Girder’s logging system was overhauled to adhere to 12-factor logging principles. Use standard idiomatic Python logging everywhere now, e.g.:

import logging
logger = logging.getLogger(__name__)
logger.info('My message')

The girder.logprint function was removed, and Girder will no longer write log messages to local files on the server’s filesystem. Additionally, the API endpoints for fetching logs and log info were removed. Instead, logs are now written to standard output and it is up to the deployment environment to direct them as needed. There are a huge variety of tools and strategies for log management, so precise recommendations on log handlers are out of scope for Girder itself.

Config files removed

The girder.local.cfg and girder.dist.cfg files are no longer used. Instead, all settings should be passed in through the environment, or as command-line overrides in the case of using girder serve in development. Everything that was able to be controlled by the config file can now be controlled by environment variables; see the configuration documentation for specifics on how to set these.

A notable change here is the configuration of the caching modules, which was previously done via the config file alone. Now, the caching modules are configured via environment variables. e.g.

GIRDER_SETTING_CORE_CACHE_ENABLED=true
GIRDER_SETTING_CORE_CACHE_CONFIG='{"cache.global.backend": "dogpile.cache.redis", "cache.global.expiration_time": 7200}'

Config keys prefixed by cache.global. are used to configure the global dogpile cache, and keys prefixed by cache.request. are used to configure the request cache.

CherryPy specific settings are now passed via environment variables as well. List of settings that can be configured:

  • GIRDER_HOST corresponds to server.socket_host

  • GIRDER_PORT corresponds to server.socket_port

  • GIRDER_THREAD_POOL corresponds to server.thread_pool

  • GIRDER_MONGO_URI corresponds to database.uri

  • GIRDER_MONGO_REPLICA_SET corresponds to database.replica_set

WSGI app for production deployments

Through load testing (as well as production usage), we discovered that cherrypy’s built-in server is not suitable for production use. It is not as performant as a dedicated WSGI server, and some fraction of requests fail ungracefully under moderate load. As such, the girder serve command is now only suitable for development and testing.

For production deployments, Girder now exposes a well-behaved WSGI app at girder.wsgi:app. Use a WSGI server such as gunicorn or uwsgi to serve in production. See the deployment documentation for an example gunicorn invocation.

Removal of the GridFS assetstore type

When we originally created the GridFS assetstore, it seemed like a reasonable blob storage solution. However, in 2024, it is no longer a recommended solution for Girder. We have removed the GridFS assetstore adapter type from the core codebase. If you are using GridFS, we recommend migrating your data to a Filesystem or S3 assetstore type and deleting your GridFS assetstore prior to upgrading to major version 5. There are many offerings in the cloud that support either the S3 or filesystem assetstore adapter in a scalable way.

Python version support

Girder will now only maintain support for CPython versions that have not reached their end-of-life. Check this page to see the current status of upstream Python version support. Note that this means we will feel free to use newer language features in core as soon as they are available in the oldest supported version.

Changes to celery configuration in Worker plugin

As we move to using celery in a more normal way, we now configure the celery app via the same code path in both Girder server and the celery worker. Because we need to support deployment topologies where the workers cannot communicate directly with the database, we cannot store celery configuration in the mongo database. Instead, celery connectivity is now always configured via the following environment variables:

  • GIRDER_WORKER_BROKER: The URL of the message broker to use for celery

  • GIRDER_WORKER_BACKEND: The URL of the result backend to use for celery

Switch to HttpOnly cookies in core web client

The auth.cookie symbol has been removed from the core web client. Cookies sent by the server will now be set with the HttpOnly flag. This means that the cookies will no longer be accessible to JavaScript, which is a security best practice. If you were relying on the auth.cookie symbol in your plugin, you should now use the auth.token symbol instead.

Note that cookies will still be sent and will still work for read-only endpoints that have set the cookie=True property on their @access decorator.

Timezone aware datetime objects

Due to deprecated behavior in Python 3.12, Girder now uses timezone-aware datetime objects. If your code relied on the old behavior of naive datetimes, you may need to update it to handle timezone-aware datetimes.

Removal of girder-worker command

The girder-worker command has been removed because it didn’t have the full configuration powers of the underlying celery worker command. Instead, run celery worker directly:

celery -A girder_worker.app worker

2.x → 3.x

Girder 3.0 changed how plugins are installed and loaded into the runtime environment. These changes require that all plugins exist as a standalone python package. Automatic loading of plugins from Girder’s plugins directory is no longer supported. This also applies to plugins that have no server (python) component.

General plugin migration steps

The following is a list of changes that are necessary for a typical Girder plugin:

  • Create a setup.py in the root directory of your plugin. A minimal example is as follows:

    from setuptools import setup
    setup(
        name='example-plugin',            # The name registered on PyPI
        version='1.0.0',
        description='An example plugin.', # This text will be displayed on the plugin page
        packages=['example_plugin'],
        install_requires=['girder'],      # Add any plugin dependencies here
        entry_points={
          'girder.plugin': [              # Register the plugin with girder.  The next line registers
                                          # our plugin under the name "example".  The name here must be
                                          # unique for all installed plugins.  The right side points to
                                          # a subclass of GirderPlugin inside your plugin.
              'example = example_plugin:ExamplePlugin'
          ]
        }
    )
    
  • Move your plugin’s python source code from the server directory to the package name defined in your setup.py. In this example, you would move all python files from server to a new directory named example_plugin.

  • Move your web_client directory under the directory created in the previous step, which was example_plugin in the previous step.

  • Create a package.json file inside your web_client directory defining an npm package. A minimal example is as follows:

    {
        "name": "@girder/my-plugin",
        "version": "1.0.0",
        "peerDepencencies": {
            "@girder/other_plugin": "*",      // Peer dependencies should be as relaxed as possible.
                                              // Add in any other girder plugins your plugin depends
                                              // on for web_client code.
                                              // Plugin dependencies should also be listed by entrypoint
                                              // name in "girderPlugin" as shown below.
            "@girder/core": "*"               // Your plugin will likely depend on girder/core.
        },
        "dependencies": {},                   // Any other dependencies of the client code
        "girderPlugin": {
            "name": "example",                // The entrypoint name defined in setup.py.
            "main": "./main.js"               // The plugin client entrypoint containing code that is executed on load.
            "webpack": "webpack.helper",      // If your plugin needs to modify the webpack config
            "dependencies": ["other_plugin"]  // If you plugin web_client requires another plugin
        }
    }
    
  • Delete the plugin.json file at the root of your plugin. Move the dependencies from that file to the top level dependencies key of the package.json file created in the previous step.

  • Create a subclass of girder.plugin.GirderPlugin in your plugin package. This class can be anywhere in your package, but a sensible place to put it is in the top-level __init__.py. There are hooks for custom behavior in this class, but at a minimum you should move the old load method into this class and point to an npm package name containing your web client code.

    from girder.plugin import getPlugin, GirderPlugin
    
    class ExamplePlugin(GirderPlugin):
        DISPLAY_NAME = 'My Plugin'              # a user-facing plugin name, the plugin is still
                                                # referenced internally by the entrypoint name.
        CLIENT_SOURCE_PATH = 'web_client'       # path to the web client relative to the python package
    
        def load(self, info):
            getPlugin('mydependency').load(info)  # load plugins you depend on
    
            # run the code that was in the original load method
    

Warning

The plugin name was removed from the info object. Where previously used, plugins should replace references to info['name'] with a hard-coded string.

  • Migrate all imports in Python and Javascript source files. The old plugin module paths are no longer valid. Any import reference to:

    • girder.plugins in Python must be changed to the actual installed module name

      • For example, change from girder.plugins.jobs.models.job import Job to from girder_jobs.models.job import Job

    • girder_plugins in Javascript must be changed to the actual installed package name

      • For example, change import { JobListWidget } from 'girder_plugins/jobs/views'; to import { JobListWidget } from '@girder/jobs/views';

    • girder in Javascript must be changed to @girder/core

      • For example, change import { restRequest } from 'girder/rest'; to import { restRequest } from '@girder/core/rest';

Other backwards incompatible changes affecting plugins

  • Automatic detection of mail templates has been removed. Instead, plugins should register them in their load method with girder.utility.mail_utils.addTemplateDirectory().

  • The mockPluginDir methods have been removed from the testing infrastructure. If plugins need to generate a one-off plugin for testing, they can generate a subclass of girder.plugin.GirderPlugin in the test file and register it in a test context with the test_plugin mark. For example,

    class FailingPlugin(GirderPlugin):
        def load(self, info):
            raise Exception('This plugin fails on load')
    
    @pytest.mark.plugin('failing_plugin', FailingPlugin)
    def test_with_failing_plugin(server):
        # the test plugin will be installed in this context
    
  • When running the server in testing mode (girder serve --mode=testing), the source directory is no longer served. If you need any assets for testing, they have to be installed into the static directory during the client build process.

  • Automatic registration of plugin models is no longer provided. If your plugin contains any custom models that must be resolved dynamically (with ModelImporter.model(name, plugin=plugin)) then you must register the model in your load method. In the jobs plugin for example, we register the job model as follows:

    from girder.utility.model_importer import ModelImporter
    from .models.job import Job
    
    class JobsPlugin(GirderPlugin):
        def load(self, info):
            ModelImporter.registerModel('job', Job, 'jobs')
    
  • In the web client, girder.rest.restRequest no longer accepts the deprecated path parameter; callers should use the url parameter instead. Callers are also encouraged to use the method parameter instead of type.

  • The CMake function add_standard_plugin_tests can not detect the python package of your plugin. It now requires you pass the keyword argument PACKAGE with the package name. For example, the jobs plugin plugin.cmake file contains the following line:

    add_standard_plugin_tests(PACKAGE "girder_jobs")
    

Client build changes

The girder_install command has been removed. This command was primarily used to install plugins and run the client build. Plugins should now be installed (and uninstalled) using pip directly. For the client build, there is a new command, girder build. Without any arguments this command will execute a production build of all installed plugins. Executing girder build --dev will build a development install of Girder’s static assets as well as building targets only necessary when running testing.

The new build process works by generating a package.json file in girder/web_client from the template (girder/web_client/package.json.template). The generated package.json itself depends on the core web client and all plugin web clients. The build process is executed in place (in the Girder Python package) in both development and production installs. The built assets are installed into a virtual environment specific static path {sys.prefix}/share/girder.

Static public path is required during web client build

The static public path, indicating the base URL where web client files are served from, must now be known when the web client is built. Most deployments can simply accept the default value of /static, unless serving Girder from a CDN or mounting at a subpath using a reverse proxy.

The static public path may be changed via the config file:

[server]
static_public_path = "/someprefix/static"

If the static public path setting is changed, the web client must be immediately rebuilt.

The static public path setting replaces all previous “static root” functionality. Accordingly:

  • The server now serves all static content from /static. The GIRDER_STATIC_ROUTE_ID constant has been removed.

  • In the server, girder.utility.server.getStaticRoot has been removed.

  • In the web client, girder.rest.staticRoot, girder.rest.getStaticRoot, and girder.rest.setStaticRoot have been removed.

  • The ability to set the web client static root / public path via the special element <div id="g-global-info-staticroot"> has been removed

Server changes

The GET /user API endpoint is only open to logged-in users

This is a policy change from 2.x, in which this endpoint was publicly accessible. Since user name data may be considered sensitive, and many Girder instances have controlled registration policies, it made sense to change this access policy.

ModelImporter behavior changes

The girder.utility.model_importer.ModelImporter class allows model types to be mapped from strings, which is useful when model types must be provided by users via the REST API. In Girder 2, there was logic to infer automatically where a model class resides without having to explicitly register it, but that logic was removed. If your plugin needs to expose a Model subclass for string-based lookup, it must be explicitly registered, e.g.

class MyModel(Model):
   ...

ModelImporter.registerModel('my_plugin_model', MyModel, plugin='my_plugin')

The load method of your plugin is a good place to register your plugin’s models.

In addition to explicitly requiring registration, the API of registerModel() has also changed. Before, one would pass the model instance, but now, one passes the model class.

# Girder 2:
ModelImporter.registerModel('my_thing', MyThing())

# Girder 3:
ModelImporter.registerModel('my_thing', MyThing)

Additionally, several key base classes in Girder no longer mixin ModelImporter, and mixing it in is now generally discouraged. So instead of self.model, just use ModelImporter.model if you must convert a string to a model instance. The following base classes are affected:

  • girder.api.rest.Resource

  • girder.models.model_base.Model

  • girder.utility.abstract_assetstore_adapter.AbstractAssetstoreAdapter

Multipart-encoded upload chunk support has been removed

Prior to version 3, Girder supported multipart/form-data content type for passing fields into the POST /file/chunk endpoint. This has been deprecated since v2.2, and has been removed. Now, the uploadId and offset fields should be passed in the query string, and the chunk data should be passed as the request body.

Event bindings are now unique by handler name

In Girder 2, it was possible to bind multiple handler callbacks to the same event with the same handler name. This has changed in Girder 3; for any given event identifier, each callback must be bound to it with a unique handler name. Example:

def cb(event):
   print('hello')

for _ in range(5):
  events.bind('an_event', 'my_handler', cb)

# Prints 'hello' five times in Girder 2, but only once in Girder 3
events.trigger('an_event')

In the new behavior, a call to bind with the same event name and handler name as an existing handler will be ignored, and will emit a warning to the log. If you wish to overwrite the existing handler, you must call girder.events.unbind() on the existing mapping first.

def a(event):
  print('a')

def b(event):
  print('b')

events.bind('an_event', 'my_handler', a)
events.bind('an_event', 'my_handler', b)

# Prints 'a' and 'b' in Girder 2, but only 'a' in Girder 3
events.trigger('an_event')

Async keyword arguments and properties changed to async_ PR #2817

In version 3.7 of python async is a reserved keyword argument. To mitigate any issues all instances of async in the codebase have changed to asynchronous. This affects:

  • The event framework girder/events.py

  • The built-in job plugin plugins/jobs/girder_jobs/models/job.py

Storing girder.local.cfg inside the package directory is no longer supported

In order to facilitate the ability to upgrade Girder using pip, the user configuration file can no longer be stored inside the package directory since it would be deleted on upgrade. Users must now store their configuration in one of the approved locations, or use GIRDER_CONFIG to specify the exact location. See the configuration documentation for more details.

Invoking Girder and Girder Client with python -m is no longer supported

Using python -m girder and python -m girder-cli was deprecated in Girder 2.5 and is no longer supported. Users are expected to have the appropriate packages installed and then use girder serve and girder-client respectively.

Removed insecure sha512 password hashing

The core.hash_alg and core.bcrypt_rounds configuration parameters were also removed. Password hashing now always occurs with 12-round bcrypt. Please reach out to us on Github Discussions <https://github.com/orgs/girder/discussions>`_ if you have existing databases with sha512 passwords or believe you need to configure bcrypt to use additional rounds.

Core setting constants now reside in settings.py

The SettingKey and SettingDefault classes (which contain constants for core settings) must now be imported from the girder.settings module.

from girder.settings import SettingDefault, SettingKey

The API for sending email has changed

The mail_utils.sendEmail function has been replaced with several new functions: mail_utils.sendMailSync, mail_utils.sendMail, mail_utils.sendMailToAdmins, mail_utils.sendMailIndividually. Note that the argument order and expected types have changed. See function documentation for details on the new usage.

Removed or moved plugins

Many plugins were either deleted from the main repository, or moved to other repositories. Plugins are no longer installable via a [plugins] extra when installing the girder Python package; rather, all are installed by pip install girder-[plugin_name]. If you were depending on a plugin that was deleted altogether, please reach out to us on Github Discussions for discussion of a path forward.

The following plugins were deleted:

  • celery_jobs

  • item_previews

  • jquery_widgets

  • metadata_extractor

  • mongo_search

  • provenance

  • treeview

  • vega

The following plugins were moved to different repositories:

1.x → 2.x

Existing installations may be upgraded to the latest 2.x release by running pip install -U girder<3 and re-running girder-install web. You may need to remove node_modules directory from the installed girder package if you encounter problems while re-running girder-install web. Note that the prerequisites may have changed in the latest version: make sure to review System Dependency Reference prior to the upgrade.

Server changes

  • The deprecated event 'assetstore.adapter.get' has been removed. Plugins using this event to register their own assetstore implementations should instead just call the girder.utility.assetstore_utilities.setAssetstoreAdapter method at load time.

  • The 'model.upload.assetstore' event no longer supports passing back the target assetstore by adding it to the event.info dictionary. Instead, handlers of this event should use event.addResponse with the target assetstore as the response.

  • The unused user parameter of the updateSize methods in the collection, user, item, and folder models has been removed.

  • The unused user parameter of the isOrphan methods in the file, item, and folder models has been removed.

  • Several core models supported an older, nonstandard kwarg format in their filter method. This is no longer supported; the argument representing the document to filter is now always called doc rather than using the model name for the kwarg. If you were using positional args or using the filtermodel decorator, this change will not affect your code.

  • Multiple configurable plugin loading paths are no longer supported. Use girder-install plugin <your_plugin_path> to install plugins that are not already in the plugins directory. Pass -s to that command to symlink instead of copying the directory. This also means:

    • The plugins.plugin_directory and plugins.plugin_install_path config file settings are no longer supported, but their presence will not cause problems.

    • The defaultPluginDir, getPluginDirs, getPluginParentDir methods inside girder.utility.plugin_utilities were removed.

    • All of the methods in girder.utility.plugin_utilities no longer accept a curConfig argument since the configuration is no longer read.

  • The girder.utility.sha512_state module has been removed.

  • The girder.utility.hash_state module has been made private. It should not be used downstream.

Web client changes

  • In version 1.x, running npm install would install our npm dependencies, as well as run the web client build process afterwards. That is no longer the case; npm install now only installs the dependencies, and the build is run with npm run build.

    • The old web client build process used to build all available plugins in the plugin directory. Now, running npm run build will only build the core code. You can pass a set of plugins to additionally build by passing them on the command like, e.g. npm run build -- --plugins=x,y,z.

    • The grunt watch command has been deprecated in favor of npm run watch. This also only watches the core code by default, and if you wish to also include other plugins, you should pass them in the same way, e.g. npm run watch -- --plugins=x,y,z.

    • The girder-install web command is now the recommended way to build web client code. It builds all enabled plugins in addition to the core code. The ability to rebuild the web client code for the core and all enabled plugins has been exposed via the REST API and the admin console of the core web client. The recommended process for administrators is to turn on all desired plugins via the switches, click the Rebuild web code button, and once that finishes, click the button to restart the server.

  • JadePug rename: Due to trademark issues, our upstream HTML templating engine was renamed from Jade to Pug. In addition, this rename coincides with a major version bump in the language which comes with notable breaking changes.

    • Template files should now end in .pug instead of .jade. This affects how they are imported as modules in webpack.

    • Jade-syntax interpolation no longer works inside string values of attributes. Use ES2015-style string templating instead. Examples:

      • a(href="#item/#{id}/foo")a(href=`#item/${id}/foo`)

      • .g-some-element(cid="#{obj.cid}").g-some-element(cid=obj.cid)

    • Full list of breaking changes are listed here, though most of the others are relatively obscure.

  • Testing specs no longer need to manually import all of the source JS files under test. We now have better source mapping in our testing infrastructure, so it’s only necessary to import the built target for your plugin, e.g.

    • 1.x:

      girderTest.addCoveredScripts([
          '/static/built/plugins/jobs/templates.js',
          '/plugins/jobs/web_client/js/misc.js',
          '/plugins/jobs/web_client/js/views/JobDetailsWidget.js',
          '/plugins/jobs/web_client/js/views/JobListWidget.js'
      ]);
      
    • 2.x:

      girderTest.importPlugin('jobs');
      
  • Build system overhaul: Girder web client code is now built with Webpack instead of uglify, and we use the Babel loader to enable ES2015 language support. The most important result of this change is that plugins can now build their own targets based on the Girder core library in a modular way, by importing specific components. See the plugin development guide for a comprehensive guide on developing web-client plugins in the new infrastructure.

Python client changes

  • Girder CLI: Subcommands are no longer specified with the -c option. Instead, the subcommand is specified just after all the general flags used for connection and authentication. For example:

    • Before: girder-cli --api-key=abcdefg --api-url=https://mygirder.org/api/v1 -c upload 1234567890abcdef ./foo

    • After: girder-cli --api-key=abcdefg --api-url=https://mygirder.org/api/v1 upload 1234567890abcdef ./foo

  • The blacklist and dryrun kwargs are no longer available in the GirderClient constructor because they only apply to uploading. If you require the use of a blacklist, you should now pass it into the upload method. These options can still be passed on the CLI, though they should now come after the upload subcommand argument.

  • Legacy method names in the GirderClient class API have been changed to keep naming convention consistent.

    • add_folder_upload_callbackaddFolderUploadCallback

    • add_item_upload_callbackaddItemUploadCallback

    • load_or_create_folderloadOrCreateFolder

    • load_or_create_itemloadOrCreateItem

  • All kwargs to GirderClient methods have been changed from snake_case to camelCase for consistency.

  • Listing methods in the GirderClient class (e.g. listItem) have been altered to be generators rather than return lists. By default, they will now iterate until exhaustion, and callers won’t have to pass limit and offset parameters unless they want a specific slice of the results. As long as you are just iterating over results, this will not break your existing code, but if you were using other operations only available on lists, this could break. The recommended course of action is to modify your logic so that you only require iteration over the results, though it is possible to simply wrap the return value in a list() constructor. Use caution if you use the list() method, as it will load the entire result set into memory.

Built-in plugin changes

  • Jobs: The deprecated jobs.filter event was removed. Use the standard exposeFields and hideFields methods on the job model instead.

  • OAuth: For legacy backward compatibility, the Google provider was previously enabled by default. This is no longer the case.