Page MenuHomePhabricator

Automated MediaWiki upgrade tool that handles updating the files
Open, Needs TriagePublic

Description

Depending on how MediaWiki has been installed, upgrading involves either doing a git checkout of the new branch (or git pull if one is running master) for MediaWiki core, extensions and skins, then running Composer for everything; or fetching MediaWiki from releases.wikimedia.org and the rest from ExtensionDistributor, and replacing the files. It would be nice if we could provide a tool that does that.

See also:

Event Timeline

I am here to publicize my script. This script use and depend the API of MediaWiki.org. This means, the script can't upgrade extensions that are not in this list.

I've used this script in the past. It's a command-line tool to be used from the root directory of your MediaWiki installation ($IP). It shows what's already managed by git and what's not. It will show you a colored output of what needs updating. Since extensions could come from anywhere, it doesn't attempt to guess where non-versioned code comes from.

<?php

/**
 * Script for discovering and upgrading all MediaWiki extensions
 * 
 * @author Greg Rundlett greg@eQuality-Tech.com
 * @copyright 2016 
 * @license GPLv3 or later
 *
 * INSTALL
 * Place the script in the installation root of your MediaWiki instance i.e. "$IP"
 *
 * CONFIG
 * The script must be configured to specify which Release you are currently running
 * and the Release you want to upgrade to.
 * 
 * You can also tell it whether you want to run composer with '--no-dev' (default)
 *
 * USAGE
 * This script expects that you are currently using some checkout of a MediaWiki Core release
 * and you want to move from that "old" release to a "new" release.
 *
 * Configure the $oldRelease and $newRelease variables to branch names found in 
 * MediaWiki core and extensions.
 * 
 * php inspect-extensions.php 
 *
 * SIDE-EFFECTS
 * This script will run a git fetch on all your (git-controlled) extensions.
 * 
 * LIMITATIONS
 * This script will pass over any extension that doesn't have a .git folder
 * It assumes that these extensions were 
 *    * installed via Composer (without the --prefer-source option)
 *    * developed locally or from non-git sources
 *    * or downloaded as a package and we don't assume where you want to get it from
 * 
 * This script can and will inadvertently change your checkout for extensions
 * which were installed using Composer and the --prefer-source option. In those
 * cases, redo a manual checkout to the branch that composer wants.
 * 
 * This script runs on GNU/Linux. No attempt has been made to make it XP
 *
 */
 
///////////////////////////////////////////////////////////////////////////////
//                   BEGIN CONFIGURATION                                     //
///////////////////////////////////////////////////////////////////////////////

// set the current and next release to upgrade to.
// it is not recommended to skip releases
$oldRelease = 'REL1_27';
$newRelease = 'REL1_28';

// if true, add composer "require-dev" modules
// if false, 'composer [install|update] --no-dev' will be used
$devMode = false; 

///////////////////////////////////////////////////////////////////////////////
//                  END OF CONFIGURATION                                     //
///////////////////////////////////////////////////////////////////////////////

// Security
// DO NOT ALLOW THIS SCRIPT TO BE CALLED FROM THE WEB !
(PHP_SAPI !== 'cli' || isset($_SERVER['HTTP_USER_AGENT'])) && die('eQuality-Tech.com can upgrade your wiki');


// by default, do not install require-dev modules
$devSwitch = ($devMode) ? "" : ' --no-dev';

$IP = dirname(__FILE__);
//  work within the extensions directory so as not to conflict with the main composer.json
$dir = "$IP/extensions";
if (!is_dir($dir)) {
  die ('This script should be placed in your wiki root, and run from there');
} else {
  // use colors by \033[32m some colored text \033[0m
  echo "Directories marked with \033[35m * \033[0m are git controlled\n";
  echo "and decorated with their current branch matching $oldRelease\n";
}
chdir ($dir);

// https://secure.php.net/manual/en/function.glob.php
// automatically sorted (unless you tell it not to)
// using the "pattern" of $dir . '/*', we get the full path for each extension
// $directories = glob($dir . '/*' , GLOB_ONLYDIR);
// using just '*' for the pattern, we get just the extension folder name
// since we've already changed into the extensions directory
$directories = glob('*' , GLOB_ONLYDIR);
// print_r($directories);
$doUpgrade = true;
$doUpgrade = false;

foreach ($directories as $ext) {
  $hasGit = false;      // detect presence of .git directories
  $hasLock = false;     // detect prior use of 'composer install'
  $gitBranch = '';      // what branch is in use currently
  $hasOldRelease = '';  // is $oldRelease the current branch?
  $upgradable = false;  // does $newRelease exist upstream?
  $hasComposer = false; // does the extension use Composer?
  
  if (is_dir("{$ext}/.git")) {
    $hasGit = true;
  }
  echo ($hasGit)? "\033[36m$ext\033[0m" : "\033[31m$ext\033[0m";
  // we can ONLY operate on an extension directory that is git controlled, 
  // otherwise, all git commands will 'see' the git repo of core
  if ($hasGit) {
    chdir ("$IP/extensions/$ext");
    $gitBranch = `git branch | grep '^*' | awk '{print $2}'`;
    $gitBranch = trim($gitBranch);
    // see if oldRelease is available locally
    $hasOldRelease = `git branch --list $oldRelease | grep '^*' | awk '{print $2}'`;
    $hasOldRelease = trim($hasOldRelease);
    // print the branch in orange (warning) if it doesn't match the expected 'oldRelease'
    echo ($gitBranch == $hasOldRelease) ? " \033[36m$gitBranch\033[0m" : " \033[33m$gitBranch\033[0m";
    // git branch -r doesn't know about remote branches it hasn't fetched yet
    exec('git fetch $1 > /dev/null');
    // add the -r option look at those remote branches
    $upgradable = `git branch -r --list "*$newRelease"`;
    // could be a multi-line string in the case of multiple remotes
    // take off leading whitespace and the last newline, and convert to an array
    $upgradable = trim($upgradable);
    if (strlen($upgradable)) {
      $upgradable = explode("\n", $upgradable);
      if (count($upgradable)) { echo "\033[35m upgradable \033[0m" ; }
      echo implode($upgradable);
      // blindly take the first remote if more than one
      $cmd = "git checkout -b $newRelease {$upgradable[0]}";

      if ($doUpgrade) {
        exec($cmd);
      } else {
        echo " $cmd";
      }
      // see if this extension has it's own composer.json
      $hasComposer = glob('composer.json');
      $hasLock = glob('composer.lock');
      $cmd = ($hasLock)? "composer update $devSwitch" : "composer install $devSwitch";
      if ($doUpgrade && $hasComposer) {
        exec($cmd);
      } elseif ($hasComposer) {
        echo " && $cmd";
      } 
    }
  }
  chdir ("$IP/extensions");
  echo "\n";
}

Why not just use Composer? Then it's only the core checkout that's required.

It works pretty well as a means to update extension files (as well as their dependencies). I know it's not officially supported, but it's easy to add a require line to composer.local.json and then run composer update. Doing so doesn't activate the extension (other than some SMW ones, which actively want the load-if-installed behaviour).

The main objection I've heard to using Composer is that pulling code from remote hosts into production is a bad idea. Which it is of course, but Composer should rather be thought of as an improvement on manually going into each extension's directory and running git pull.

This would be super awesome for third-party system administrators!