release.sh 10.6 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
if (count($argv) < 3) {
    echo "Usage is ${argv[0]} [version] [branch] [<patchfile>...]";
    echo "e.g. ${argv[0]} 16.04.3 16.04_STABLE";
    echo "e.g. ${argv[0]} 15.10.1 15.10_STABLE";
    exit(1);
21 22
}

23 24
# Check for git gpg lp-project-upload

25 26 27 28
if (!@is_executable('/usr/bin/gpg')) {
  echo "You need to install gpg: apt-get install gnupg";
  exit(1);
}
29

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

35 36 37 38
if (!@is_executable('/usr/bin/lp-project-upload')) {
  echo "You need to install lp-project-upload: apt-get install ubuntu-dev-tools (maverick or earlier) or lptools";
  exit(1);
}
39

40 41 42 43
if (!@is_executable('/usr/bin/m4')) {
  echo "You need to install m4: apt-get install m4";
  exit(1);
}
44

45 46
$GIT_MAJOR = `git --version | cut -d' ' -f 3 | cut -d'.' -f 1`;
$GIT_MINOR = `git --version | cut -d' ' -f 3 | cut -d'.' -f 2`;
47

48 49 50 51
if ($GIT_MAJOR < 1 || ($GIT_MAJOR == 1 && $GIT_MINOR < 6 )) {
  echo "Your version of git is too old. Install git 1.6.";
  exit(1);
}
52 53 54

# Check all parameters

55
$VERSION=$argv[1];
56

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

66
$BRANCH = $argv[2];
67

68 69 70 71 72 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
// 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);
    }
}
98

99 100 101
$BUILDDIR = trim(`mktemp -d /tmp/mahara.XXXXX`);
$CURRENTDIR = getcwd();
$SCRIPTDIR = dirname(__FILE__);
102

103 104
mkdir("${BUILDDIR}/mahara", 0777, true);
chdir("${BUILDDIR}/mahara");
105

106 107 108
# Main Mahara repo to pull from
$PUBLIC="git@github.com:MaharaProject/mahara.git";
$PUBLIC = "https://git.mahara.org/mahara/mahara.git";
109

110 111 112 113 114 115
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");
116

117 118 119 120 121
# 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);
}
122

123 124
// This is a separate variable for historical reasons
$RELEASE = $VERSION;
125

126 127 128
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");
129

130 131 132 133 134 135 136 137
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");
}
138

139 140
# Add a version bump commit for the release
$VERSIONFILE='htdocs/lib/version.php';
141

142
# If there's no 'micro' part of the version number, assume it's a stable release, and
143 144 145 146 147 148
# 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;
149

150 151
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}");
152

153 154 155
echo "\n\n";
passthru("git add ${VERSIONFILE}");
passthru("git commit -s -m \"Version bump for $RELEASE\"");
156 157

# Tag the version bump commit
158 159 160 161 162 163 164 165 166 167 168 169 170 171
$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";
    echo `make css >> ../css.log 2>&1`;
    if (!file_exists('htdocs/theme/raw/style/style.css')) {
        echo "CSS files did not build correctly! Check $BUILDDIR/css.log for details.";
        exit(1);
    }
}
172

173
# Package up the release
174 175 176 177
$PACKAGEDIR = 'mahara-' . $VERSION;
echo "Package directory: $BUILDDIR/$PACKAGEDIR\n";
passthru("cp -r $BUILDDIR/mahara $BUILDDIR/$PACKAGEDIR");
chdir("$BUILDDIR/$PACKAGEDIR");
178

179
# Delete everything that shouldn't be included
180
if (getcwd() != "$BUILDDIR/$PACKAGEDIR" || $PACKAGEDIR == '') {
181 182 183 184
    echo "Couldn't cd into the right directory";
    exit(1);
}
passthru('find . -type d -name ".git" -execdir rm -Rf {} \; 2> /dev/null');
185
passthru('find . -type f -name ".gitignore" -execdir rm -Rf {} \; 2> /dev/null');
186 187 188 189 190 191 192 193 194
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");
195
passthru("rm -Rf ChangeLog.back");
196 197

# Get the location for all phpunit directories
198
$phpunitdirs = explode("\n", `find . -type d -name 'phpunit' -path '*/tests/phpunit' 2> /dev/null`);
199 200 201 202
foreach ($phpunitdirs as $dir) {
    $parentdir = dirname($dir);
    # Determine whether the parent directory contains anything other than
    # phpunit. If not, remove the whole parent directory.
203
    $siblings = explode("\n", `find "$parentdir" -maxdepth 1 -mindepth 1 2> /dev/null`);
204 205 206 207 208 209 210
    if (count($siblings) == 1) {
        passthru("rm -Rf $parentdir");
    }
    else {
        passthru("rm -Rf $dir");
    }
}
211

212 213 214 215 216 217 218
# 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";
219
passthru("zip -rq ${CURRENTDIR}/mahara-${RELEASE}.zip $PACKAGEDIR");
220 221


222
# Save git changelog
223 224 225 226 227 228 229 230 231 232
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 = '';
}
233

234 235
# Prepare release notes
// TODO: Replace this with a simple find/replace, to remove the m4 dependency
236
$TMP_M4_FILE = '/tmp/mahara-releasenotes.m4.tmp';
237 238 239 240 241
$m4script = <<<STRING
changecom
define(\\`__RELEASE__',\\`${RELEASE}')dnl
define(\\`__OLDRELEASE__',\\`${OLDRELEASE}')dnl
define(\\`__MAJOR__',\\`${MAJOR}')dnl
242

243 244 245 246 247 248 249 250 251
STRING;
file_put_contents($TMP_M4_FILE, $m4script);
if ($releasecandidate) {
    $TEMPLATE = 'releasenotes.rc.template';
}
else {
    $TEMPLATE = 'releasenotes.stable.template';
}
passthru("m4 ${TMP_M4_FILE} ${SCRIPTDIR}/${TEMPLATE} > ${CURRENTDIR}/releasenotes-${RELEASE}.txt");
252

253
# Second version bump for post-release
254 255
$NEWVERSION = $NEWVERSION + 1;
$NEWRELEASE = $MAJOR . ($releasecandidate ? 'rc' : '.') . ($MINOR + 1) . "testing";
256

257 258
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}");
259

260 261
passthru("git add ${VERSIONFILE}");
passthru("git commit -s -m \"Version bump for $NEWRELEASE\"");
262

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

267
# Output commands to push to the remote repository and clean up
268 269 270
$CLEANUPSCRIPT = "$CURRENTDIR/release-${RELEASE}-cleanup.sh";
$cleanup  = <<<CLEANUP
#!/bin/sh
271

272
set -e
273

274 275 276
cd ${BUILDDIR}/mahara
git push gerrit ${BRANCH}:refs/heads/${BRANCH}
git push gerrit ${RELEASETAG}:refs/tags/${RELEASETAG}
277

278 279 280
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
281

282 283 284 285
cd ${CURRENTDIR}
lp-project-upload mahara ${RELEASE} mahara-${RELEASE}.tar.gz
lp-project-upload mahara ${RELEASE} mahara-${RELEASE}.tar.bz2
lp-project-upload mahara ${RELEASE} mahara-${RELEASE}.zip
286

287 288 289 290
echo
echo \"All done. Once you've checked that the files were uploaded successfully, run this:\"
echo \"  rm -rf ${BUILDDIR}\"
CLEANUP;
291

292 293
file_put_contents($CLEANUPSCRIPT, $cleanup);
chmod($CLEANUPSCRIPT, 0700);
294 295

# Clean up
296 297 298 299 300 301 302 303 304 305 306 307
passthru("rm ${VERSIONFILE}.temp");
passthru("rm ${CURRENTDIR}/ChangeLog.temp");
passthru("rm ${CURRENTDIR}/changes.temp");
passthru("rm ${TMP_M4_FILE}");

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";