release.php 12.3 KB
Newer Older
1 2 3 4 5
#!/usr/bin/php
<?php
define('INTERNAL', 1);
define('CLI', 1);

6 7 8
#
# Builds release tarballs of Mahara at the given version, ready for
# distribution
9
#
10 11 12 13
# If you're doing a release which has security fixes, add the names
# of the patches to the end of the command line, and the script will
# apply the patches before committing the version bumps and editing
# the changelog.
14
#
15

16 17 18 19 20 21 22
$usage = <<<STRING
Usage is ${argv[0]} [version] [branch] [<changesetnumber>...]
e.g. ${argv[0]} 16.04.3 16.04_STABLE
e.g. ${argv[0]} 15.10.1 15.10_STABLE 5793 5795

STRING;

23
if (count($argv) < 3) {
24
    echo $usage;
25
    exit(1);
26 27
}

28 29
# Check for git gpg lp-project-upload

30
if (!@is_executable('/usr/bin/gpg')) {
31
  echo "You need to install gpg: apt-get install gnupg\n";
32 33
  exit(1);
}
34

35
if (!@is_executable('/usr/bin/git')) {
36
  echo "You need to install git: apt-get install git-core\n";
37 38
  exit(1);
}
39

40
if (!@is_executable('/usr/bin/lp-project-upload')) {
41
  echo "You need to install lp-project-upload: apt-get install ubuntu-dev-tools (maverick or earlier) or lptools\n";
42 43
  exit(1);
}
44

45
if (!@is_executable('/usr/bin/m4')) {
46
  echo "You need to install m4: apt-get install m4\n";
47 48
  exit(1);
}
49

50 51
$GIT_MAJOR = `git --version | cut -d' ' -f 3 | cut -d'.' -f 1`;
$GIT_MINOR = `git --version | cut -d' ' -f 3 | cut -d'.' -f 2`;
52

53
if ($GIT_MAJOR < 1 || ($GIT_MAJOR == 1 && $GIT_MINOR < 6 )) {
54
  echo "Your version of git is too old. Install git 1.6.\n";
55 56
  exit(1);
}
57 58 59

# Check all parameters

60
$VERSION=$argv[1];
61

62 63
$result = preg_match('/([0-9]+\.[0-9]+)(\.|rc)([0-9]+)/i', $VERSION, $matches);
if (!$result) {
64 65
    echo "Invalid version number. It must match the pattern \"15.04.1\" or \"15.04rc1\".\n";
    echo $usage;
66 67 68 69 70
    exit(1);
}
$MAJOR = $matches[1];
$MINOR = $matches[3];
$releasecandidate = ($matches[2] == 'rc');
71

72
$BRANCH = $argv[2];
73

74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
// Check for unmerged drafts
if ($releasecandidate) {
    // If it's a release candidate, draft patches will be on the master branch still
    $draftbranch = 'master';
}
else {
    $draftbranch = $BRANCH;
}
$draftlines = explode(
    "\n",
    `ssh \$USER@reviews.mahara.org -p 29418 gerrit query is:draft branch:$draftbranch project:mahara "label:Code-Review>=0" "label:Verified>=0"`
);
$draftcount = 0;
foreach ($draftlines as $line) {
    if (preg_match("/rowCount: *([0-9]+)/", $line, $matches)) {
        $draftcount = $matches[1];
        break;
    }
}
if ($draftcount > 0) {
    $response = readline("There are Draft patches that may need to be merged. Do you want to continue with release [y/n]?");
    $response = trim(strtolower($response));
    if ($response == 'yes' || $response == 'y') {
        echo "Continuing...";
    }
    else {
        echo "Quitting out";
        exit(1);
    }
}
104

105 106 107
$BUILDDIR = trim(`mktemp -d /tmp/mahara.XXXXX`);
$CURRENTDIR = getcwd();
$SCRIPTDIR = dirname(__FILE__);
108

109 110
mkdir("${BUILDDIR}/mahara", 0777, true);
chdir("${BUILDDIR}/mahara");
111

112 113 114
# Main Mahara repo to pull from
$PUBLIC="git@github.com:MaharaProject/mahara.git";
$PUBLIC = "https://git.mahara.org/mahara/mahara.git";
115

116 117 118 119 120 121
echo "Cloning public repository ${PUBLIC} in ${BUILDDIR}/mahara\n";
passthru('git init');
passthru("git remote add -t ${BRANCH} mahara ${PUBLIC}");
passthru("git fetch -q mahara");
passthru("git checkout -b ${BRANCH} mahara/${BRANCH}");
passthru("git fetch -q -t");
122

123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152

// Applying gerrit patches named on the command line
if (count($argv) > 3) {
    $successwithpatches = true;
    for ($i = 3; $i < count($argv); $i++) {
        $patchno = $argv[$i];
        $refline = shell_exec("ssh reviews.mahara.org -p 29418 gerrit query --current-patch-set --format=TEXT change:'{$patchno}'| grep ref");
        if ($refline) {
            $result = preg_match('#ref: (refs/changes/[/0-9]+)#', $refline, $matches);
            if ($result) {
                $return_var = passthru("git fetch ssh://reviews.mahara.org:29418/mahara {$matches[1]} && git cherry-pick FETCH_HEAD");
                if ($return_var != 0) {
                    echo "Couldn't cherry-pick Gerrit change number {$patchno}.\n";
                    $successwithpatches = false;
                }
            }
            else {
                echo "Couldn't find latest patch number for Gerrit change number {$patchno}.\n";
                $succesoverall = false;
            }
        } else {
            echo "Couldn't retrieve information about Gerrit change number {$patchno}.\n";
            $successwithpatches = false;
        }
    }
    if (!$successwithpatches) {
        exit();
    }
}

153 154 155 156 157
# Edit ChangeLog
if (!file_exists("ChangeLog")) {
    echo "The ChangeLog file is missing and this is a stable release. Create an empty file called ChangeLog and commit it.";
    exit(1);
}
158

159 160
// This is a separate variable for historical reasons
$RELEASE = $VERSION;
161

162 163 164
passthru("echo \"#\n# Please add a description of the major changes in this release, one per line.\n# Don't put a dash or asterisk at the front of each line, they'll get added automatically.\n# Also, don't leave any blank lines at the bottom of this file.\n#\" > ${CURRENTDIR}/ChangeLog.temp");
passthru("sensible-editor ${CURRENTDIR}/ChangeLog.temp");
passthru("grep -v \"^#\" ${CURRENTDIR}/ChangeLog.temp > ${CURRENTDIR}/changes.temp");
165

166 167 168 169 170 171 172 173
if (file_exists("ChangeLog")) {
    copy('ChangeLog', 'ChangeLog.back');
    passthru("echo \"$RELEASE (`date +%Y-%m-%d`)\" > ChangeLog");
    passthru("sed 's/^/- /g' ${CURRENTDIR}/changes.temp >> ChangeLog");
    passthru("echo >> ChangeLog");
    passthru("cat ChangeLog.back >> ChangeLog");
    passthru("git add ChangeLog");
}
174

175 176
# Add a version bump commit for the release
$VERSIONFILE='htdocs/lib/version.php';
177

178
# If there's no 'micro' part of the version number, assume it's a stable release, and
179 180 181 182 183 184
# bump version by 1.  If it's an unstable release, use
$OLDVERSION = call_user_func(function($versionfile) {
    require($versionfile);
    return $config->version;
}, $VERSIONFILE);;
$NEWVERSION = $OLDVERSION + 1;
185

186 187
passthru("sed \"s/\$config->version = [0-9]\{10\};/\$config->version = $NEWVERSION;/\" ${VERSIONFILE} > ${VERSIONFILE}.temp");
passthru("sed \"s/\$config->release = .*/\$config->release = '$RELEASE';/\" ${VERSIONFILE}.temp > ${VERSIONFILE}");
188

189 190 191
echo "\n\n";
passthru("git add ${VERSIONFILE}");
passthru("git commit -s -m \"Version bump for $RELEASE\"");
192 193

# Tag the version bump commit
194 195 196 197 198 199 200 201
$LASTTAG = trim(`git describe --abbrev=0`);
$RELEASETAG = strtoupper($RELEASE) . '_RELEASE';
echo "\nTag new version bump commit as '$RELEASETAG'\n";
passthru("git tag -s ${RELEASETAG} -m \"$RELEASE release\"");

# Build the css
if ($OLDVERSION >= 2015091700) {
    echo "Building css...\n";
202
    passthru("make css >> ../css.log 2>&1");
203 204 205 206 207
    if (!file_exists('htdocs/theme/raw/style/style.css')) {
        echo "CSS files did not build correctly! Check $BUILDDIR/css.log for details.";
        exit(1);
    }
}
208

209
# Package up the release
210 211 212 213
$PACKAGEDIR = 'mahara-' . $VERSION;
echo "Package directory: $BUILDDIR/$PACKAGEDIR\n";
passthru("cp -r $BUILDDIR/mahara $BUILDDIR/$PACKAGEDIR");
chdir("$BUILDDIR/$PACKAGEDIR");
214

215
# Delete everything that shouldn't be included
216
if (getcwd() != "$BUILDDIR/$PACKAGEDIR" || $PACKAGEDIR == '') {
217 218 219 220
    echo "Couldn't cd into the right directory";
    exit(1);
}
passthru('find . -type d -name ".git" -execdir rm -Rf {} \; 2> /dev/null');
221
passthru('find . -type f -name ".gitignore" -execdir rm -Rf {} \; 2> /dev/null');
222 223 224 225 226 227 228 229 230
passthru('find . -type d -name "node_modules" -execdir rm -Rf {} \; 2> /dev/null');
passthru('find . -type f -name "gulpfile.js" -execdir rm -Rf {} \; 2> /dev/null');
passthru('find htdocs/theme -type d -name "sass" -execdir rm -Rf {} \; 2> /dev/null');
passthru("rm -Rf test");
passthru("rm -Rf .gitattributes");
passthru("rm -Rf Makefile");
passthru("rm -Rf phpunit.xml");
passthru("rm -Rf external");
passthru("rm -Rf package.json");
231
passthru("rm -Rf ChangeLog.back");
232 233

# Get the location for all phpunit directories
234
$phpunitdirs = explode("\n", `find . -type d -name 'phpunit' -path '*/tests/phpunit' 2> /dev/null`);
235 236 237 238
foreach ($phpunitdirs as $dir) {
    $parentdir = dirname($dir);
    # Determine whether the parent directory contains anything other than
    # phpunit. If not, remove the whole parent directory.
239
    $siblings = explode("\n", `find "$parentdir" -maxdepth 1 -mindepth 1 2> /dev/null`);
240 241 242 243 244 245 246
    if (count($siblings) == 1) {
        passthru("rm -Rf $parentdir");
    }
    else {
        passthru("rm -Rf $dir");
    }
}
247

248 249 250 251 252 253 254
# Create tarballs
chdir($BUILDDIR);
echo "Creating mahara-${RELEASE}.tar.gz\n";
passthru("tar c $PACKAGEDIR | gzip -9 > ${CURRENTDIR}/mahara-${RELEASE}.tar.gz");
echo "Creating mahara-${RELEASE}.tar.bz2\n";
passthru("tar c $PACKAGEDIR | bzip2 -9 > ${CURRENTDIR}/mahara-${RELEASE}.tar.bz2");
echo "Creating mahara-${RELEASE}.zip\n";
255
passthru("zip -rq ${CURRENTDIR}/mahara-${RELEASE}.zip $PACKAGEDIR");
256 257


258
# Save git changelog
259 260 261 262 263 264 265 266 267 268
chdir("$BUILDDIR/mahara");
if ($LASTTAG) {
    echo "Getting changelog from previous tag ${LASTTAG}\n";
    passthru("git log --pretty=format:\"%s\" --no-color --no-merges ${LASTTAG}..${RELEASETAG} > ${CURRENTDIR}/${RELEASETAG}.cl");
    $OLDRELEASE = substr($LASTTAG, 0, -1 * strlen('_RELEASE'));
}
else {
    passthru("git log --pretty=format:\"%s\" --no-color --no-merges ${RELEASETAG} > ${CURRENTDIR}/${RELEASETAG}.cl");
    $OLDRELEASE = '';
}
269

270 271
# Prepare release notes
// TODO: Replace this with a simple find/replace, to remove the m4 dependency
272
$TMP_M4_FILE = '/tmp/mahara-releasenotes.m4.tmp';
273
passthru("sed 's/^/ * /g' ${CURRENTDIR}/changes.temp >> ${CURRENTDIR}/changes.withasterisks.temp");
274 275
$m4script = <<<STRING
changecom
276 277 278
define(`__RELEASE__',`${RELEASE}')dnl
define(`__OLDRELEASE__',`${OLDRELEASE}')dnl
define(`__MAJOR__',`${MAJOR}')dnl
279
define(`__CHANGES__',`include(`${CURRENTDIR}/changes.withasterisks.temp')')dnl
280

281 282
STRING;
file_put_contents($TMP_M4_FILE, $m4script);
283

284 285 286 287 288 289 290
if ($releasecandidate) {
    $TEMPLATE = 'releasenotes.rc.template';
}
else {
    $TEMPLATE = 'releasenotes.stable.template';
}
passthru("m4 ${TMP_M4_FILE} ${SCRIPTDIR}/${TEMPLATE} > ${CURRENTDIR}/releasenotes-${RELEASE}.txt");
291

292
# Second version bump for post-release
293 294
$NEWVERSION = $NEWVERSION + 1;
$NEWRELEASE = $MAJOR . ($releasecandidate ? 'rc' : '.') . ($MINOR + 1) . "testing";
295

296 297
passthru("sed \"s/\$config->version = [0-9]\{10\};/\$config->version = $NEWVERSION;/\" ${VERSIONFILE} > ${VERSIONFILE}.temp");
passthru("sed \"s/\$config->release = .*/\$config->release = '$NEWRELEASE';/\" ${VERSIONFILE}.temp > ${VERSIONFILE}");
298

299 300
passthru("git add ${VERSIONFILE}");
passthru("git commit -s -m \"Version bump for $NEWRELEASE\"");
301

302
# Add gerrit repo, for pushing the new security patches, version bump & changelog commits
303 304
$GERRIT = "ssh://reviews.mahara.org:29418/mahara";
passthru("git remote add gerrit ${GERRIT}");
305

306
# Output commands to push to the remote repository and clean up
307 308 309
$CLEANUPSCRIPT = "$CURRENTDIR/release-${RELEASE}-cleanup.sh";
$cleanup  = <<<CLEANUP
#!/bin/sh
310

311
set -e
312

313 314 315
cd ${BUILDDIR}/mahara
git push gerrit ${BRANCH}:refs/heads/${BRANCH}
git push gerrit ${RELEASETAG}:refs/tags/${RELEASETAG}
316

317 318 319
gpg --armor --sign --detach-sig ${CURRENTDIR}/mahara-${RELEASE}.tar.gz
gpg --armor --sign --detach-sig ${CURRENTDIR}/mahara-${RELEASE}.tar.bz2
gpg --armor --sign --detach-sig ${CURRENTDIR}/mahara-${RELEASE}.zip
320

321
cd ${CURRENTDIR}
322 323 324
${CURRENTDIR}/lptools/lp-project-upload mahara ${RELEASE} mahara-${RELEASE}.tar.gz changes.withasterisks.temp releasenotes-${RELEASE}.txt
${CURRENTDIR}/lptools/lp-project-upload mahara ${RELEASE} mahara-${RELEASE}.tar.bz2 changes.withasterisks.temp releasenotes-${RELEASE}.txt
${CURRENTDIR}/lptools/lp-project-upload mahara ${RELEASE} mahara-${RELEASE}.zip changes.withasterisks.temp releasenotes-${RELEASE}.txt
325

326
echo
327 328
echo "All done. Once you've checked that the files were uploaded successfully, run this:"
echo "  rm -rf ${BUILDDIR}"
329
CLEANUP;
330

331 332
file_put_contents($CLEANUPSCRIPT, $cleanup);
chmod($CLEANUPSCRIPT, 0700);
333 334

# Clean up
335 336 337 338 339
// Let people clean these up manually. They might be useful for debugging.
// passthru("rm ${VERSIONFILE}.temp");
// passthru("rm ${CURRENTDIR}/ChangeLog.temp");
// passthru("rm ${CURRENTDIR}/changes.temp");
// passthru("rm ${TMP_M4_FILE}");
340 341 342 343 344 345 346 347

echo "\n\nTarballs, release notes & changelog for Launchpad:\n\n";
chdir($CURRENTDIR);
passthru("ls -l mahara-${RELEASE}.tar.gz mahara-${RELEASE}.tar.bz2 mahara-${RELEASE}.zip releasenotes-${RELEASE}.txt ${RELEASETAG}.cl");

echo "\n1. Check that everything is in order in the ${BUILDDIR}/mahara repository.\n";
echo "\n2. Create the release on launchpad at https://launchpad.net/mahara/+milestone/${RELEASE}\n";
echo "\n3. Run the commands in ${CLEANUPSCRIPT} to push the changes back to the remote repository and upload the tarballs.\n";