Introduction

At the University Library Frankfurt, we currently host 21 OJS journals, with more to come. Since we apply a strategy that runs only a single journal within an OJS instance, we have to maintain 21 different OJS instances. In order to maintain and manage this multiplicity, we found it important to come up with structures on the server and helper tools.

Especially the process of updating a journal instance can be quite tedious, since it involves multiple manual steps and can cause problems when forgetting something in the process. Due to these manual steps, the upgrade of a single OJS instance can take up to 20 minutes – and longer, if something doesn’t go according to plan.

To ease the upgrade process of a single OJS instance and make it less error prone, the FID Biodiversity Research and the FID Linguistics developed the ojs_updater tool. This tool is essentially a wrapper around the tools/upgrade.php script shipped with OJS that automates all the previously manual tasks, performs checks, creates backups before the upgrade (and rewinds in case of an error in the upgrade process) and gives you some additional customisation options. The tool shrinks down the time for an upgrade to 2 to 3 minutes (the actual time for running the process). It automates 7 of the 12 recommended steps of the official PKP guideline for OJS upgrades.

So, what does the ojs_updater not do?

  • It does not activate the maintenance mode automatically. There are too many possibilities and technologies out there to cover them all with this simple tool.
  • You should test the upgrade in a sandbox for yourself. Not only, if the upgrade runs successfully, but also make sure that all your plugins and styles are still working as intended.
  • You should also have the version to upgrade to available on your system. For this purpose, we have a separate script running that checks every night for new OJS releases and downloads them.

Additionally, the ojs_updater also does not handle Docker environments. Instead, it expects you to have all your journals in separate folders in the same directory (see next section)

But despite these things, the ojs_updater got you covered.

This blog post is accompanied by a git repo that includes some of the code examples shown in the text as well as some helper scripts.

Server-side Setup

Requirements

Preparations

You will need the virtual environment package for your Python 3 instance. Under Ubuntu, you would install it with: apt install python3-venv

System Setup

The ojs updater is actively tested on SUSE Linux Enterprise Server (SLES) and Ubuntu and is known to have worked at some point on other distros/operating systems (e.g. Debian, FreeBSD). However, in order to keep this guide (somewhat) clear, it is assumed that you use Apache 2.4 on Ubuntu 22.10, MariaDB and Python 3.6 or newer. It’s of course possible to use something else; you just need to modify the updater settings accordingly. However, as far as RDBMSes go, only MySQL™ and MariaDB are currently supported. Furthermore, we recommend (and assume) that all your journals are kept in separate folders under the same root directory inside the Apache document root (/usr/www/html/). This is not required for the ojs_updater to work, but makes things much easier. Depending on your operating system, the Apache document root can be at different places (/srv/www/…, /srv/http/…, /usr/local/www/data/…, …).

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/path/to/webroot/my-journals/
├── journal-1/
│   ├── config.inc.php
│   └── ...
├── journal-2/
│   ├── config.inc.php
│   └── ...
├── journal-3/
│   ├── cofig.inc.php
│   └── ...
└── ...
...

In addition to that, it is required to set up a folder structure for the ojs_updater itself. For the purpose of this tutorial, we assume that you create the folder structure under /usr/local. Other places are, of course, possible as well.

Example:

1
2
$ mkdir -p /usr/local/ojs/{versions,backup/{db,htdocs}}
$ chown -R <webserver-user>:<webserver-group> /usr/local/ojs
1
2
3
4
5
/usr/local/ojs/
├── backup    <-- ojs_backup_folder
│   ├── db    <-- ojs_backup_db
│   └── www   <-- ojs_backup_www
└── versions  <-- ojs_version_folder

Now change into the newly created ojs/ folder, create a virtual environment and activate it:

1
2
3
$ cd /usr/local/ojs/
$ python3 -m venv updater_venv
$ source updater_venv/bin/activate

After making sure you are in fact in the virtual environment, install the ojs_updater from PyPI:

1
2
3
$ command -v python3
$ command -v pip3
$ pip3 install ojs_updater

Next, we recommend to put a little shell script wrapper in /usr/local/bin/ojs-updater:

1
2
3
4
#!/bin/bash
PYTHON_CMD="/usr/local/ojs/updater_venv/bin/python"
UPDATE_PATH="/usr/local/ojs/updater_venv/bin/ojs_updater"
"$PYTHON_CMD" "$UPDATE_PATH" "$@"

This allows you to call ojs-updater directly from the terminal, without the need for activating the virtual environment each time. For convenience, the script is also readily available in the accompanying Github repo of this blog post.

Adapt the script according to your server structure and make it executable with chmod +x /usr/local/bin/ojs-updater. Next make sure, /usr/local/bin is in the PATH variable of root (using your preferred method, e.g. editing /etc/environment)

Download the OJS Release Package

Let’s assume you want to upgrade an OJS 3.2 instance to OJS 3.3.0-13. First download and extract the necessary OJS version:

1
2
3
$ cd /usr/local/ojs/versions/
$ wget https://pkp.sfu.ca/ojs/download/ojs-3.3.0-13.tar.gz
$ tar -xvf https://pkp.sfu.ca/ojs/download/ojs-3.3.0-13.tar.gz

where /usr/ojs/versions/ is the directory, where all potential OJS versions live on your system. The ojs_updater will always use the most current OJS version in this directory. The tool will never try to download any OJS version! Thus, if the latest OJS version in your /usr/ojs/versions/ is outdated, the ojs_updater will only upgrade to this outdated version! We recommend to setup a cron job that automatically downloads new OJS versions. An example script for this task is included in the example Github repo.

Configuration

The ojs_updater expects you to define your system settings once in an ojs_updater_settings.yml file, where you can set the owner and the group of your OJS instances, the path to the available OJS versions that you can upgrade to, the path to the backup directory, and a list of custom files or directories that have to be copied over during the upgrade process (e.g. themes or custom plugins). All the default migration processes (like copying the public folder and the config.inc.php) will be carried out by theojs_updater without any configuration.

We suggest to start from the example file and modify it according to your local requirements:

1
2
$ cd /etc
$ wget https://raw.githubusercontent.com/ubffm/ojs_updater/main/ojs_updater_settings.yml.example -O ojs_updater_settings.yml

Let’s assume you have a server directory structure like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/
├── etc
├── var/www/
   └── html/
       └── my-journals/
           ├── journal-1
           ├── journal-2
           └── journal-3
├── usr/local/
   └── ojs/
       └── ojs_updater/
           ├── ojs_updater_settings.yml
           └── updater_venv
└── tmp

Hence, you would set the ojs_updater_settings.yml like this:

1
2
3
4
5
6
7
ojs_version_folder: /usr/local/ojs/versions/  # Here should live the new unzipped OJS version folder 
ojs_backup_folder: /usr/local/ojs/backups/
ojs_backup_db: /usr/local/ojs/backups/db  # This has to be a subdirectory of "ojs_backup_folder"
ojs_backup_www: /usr/local/ojs/backups/htdocs  # This also!
custom_files:
    journal-2:
        - plugins/themes/theme-name/

The default setting uses wwwrun and www as owner and group of the journal directory, respectively, which is the default Apache user/group on SLES. On Ubuntu, it should be www-data and www-data. The ojs_updater will run permission checks when starting up. If on your system the journal directories are owned by another user, you can also customise this in the ojs_updater_settings.yml:

1
2
owner: www-data
group: www-data

But do not worry. If the ojs_updater realises that the OJS journal directory is not owned by the expected user, it will just stop and complain. Nothing will break!

You may also see that we decided to set the functions for both mysqldump and php explicitly. Normally, you should be fine with just setting the output that is given you by which mysqldump and which php. But there may be scenarios (e.g. having multiple PHP versions installed on the same system), where this becomes handy.

The important thing is that you set all this up only once and do not have to think about it with every OJS upgrade you do.

Walk-through

All following steps are required to be run either via sudo or as the root user. The update script itself can only run as root, but drops its privileges to the specified user/group on start-up. This is usually the Apache user (e.g. www-data, wwwrun, …). It also performs a few tests to make sure that no unforeseen permission issues occur during the actual update.

Enter Maintenance Mode

When everything is set up, we can proceed with the actual upgrading process. First, we have to put the journal into maintenance mode. How (or if) you do this, is left to you. This is mostly a precaution. The PKP upgrade guide give a recommendation how to do it on Apache servers.

Run the Upgrade

For the upgrade to start, change to the OJS root directory (/var/www/html/journals) and call: ojs-updater ./journal-2.

This will kick-off the upgrade process: checking permissions, creating backups, copying files, and finally starting the actual upgrade script shipped with OJS. The console logs are quite verbose, so you get a clear idea of what is happening.

After the update, you’ll notice that a new folder appeared (journal-2_<timestamp>) in your journal directory. This is the original folder of the upgraded journal - only renamed. This folder is just kept for convenience, in case something goes wrong. This folder should be removed once you made sure the update was successful.

If the upgrade process should fail after the database migration started (leaving you with an inconsistent database), the ojs_updater will use the backups it just created to rewind both the database and the journal directory to their original state. We do not recommend to rely on this mechanism, since we could only test it on our own system, but it should still provide you with some safety net. If the automatic rewinding does not work, the ojs_updater still provides you with the necessary backups to jump back to the last version manually. The backup files are stored at the configured location (default: /usr/local/ojs/backups.

After the upgrade is completed, the ojs_updater will terminate. Now you only need to leave maintenance mode and check the config.inc.php.OJSNEW for any new or deprecated settings. You could do so for example with vimdiff journal-2/config.inc.php journal-2/config.inc.php.OJSNEW. Please also note, that the ojs_updater does not upgrade any plugins; these are just carried over if configured. You can upgrade the journal’s plugins in the administration backend after the upgrade.

Forcing an Upgrade

The ojs_updater will automatically upgrade to the latest version available to it in the directory configured as ojs_version_folder. If you should try to upgrade an OJS instance that is already on the latest available version, the ojs_updater will complain and stop safely quite fast. However, should there be a scenario where you need to enforce an OJS update, you can use the --force parameter.

1
$ ojs_updater --force ./journal-2

This command will update the OJS instance of journal-2 to OJS 3.3.0-13 (which is the latest version in our example version directory /usr/local/ojs/versions/) even if this exact version is already running in journal-2.

Conclusion

Since its initial development, the ojs_updater has become a fundamental part of our OJS maintenance workflow, saving us a lot of manual work. We are happy to share our approach and hope that it will prove helpful to others as well. If you decide to try it out and spot some issues, we’d like to encourage you to get in touch via Github issues.

References

All tree diagram created with https://tree.nathanfriend.io