release.php 12 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.noasterisks.temp");
274
275
$m4script = <<<STRING
changecom
276
277
278
279
define(`__RELEASE__',`${RELEASE}')dnl
define(`__OLDRELEASE__',`${OLDRELEASE}')dnl
define(`__MAJOR__',`${MAJOR}')dnl
define(`__CHANGES__',`include(`${CURRENTDIR}/changes.noasterisks.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
322
323
324
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
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
340
341
342
343
344
345
346
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";