Release Management
Table of Contents
Four Principles of Low-Risk Software Releases
Release Management for the Haiti Portal
Note: As of January 2012, BZR/Launchpad info for eden is deprecated. Please visit the GitHub page. Thanks.
This example can be updated/adapted for future instances.
We have 3 running instances:
- Production: http://haiti.sahanafoundation.org
- UAT: http://haiti-test.sahanafoundation.org - lifeeth
- Development: http://haiti-dev.sahanafoundation.org - lifeeth
The Bug Tracker should have different views to distinguish between bugs being reported against each version
- ToDo customise the 'Report Bugs' footer URL to link directly to a new ticket against that version?
We have several Code branches:
- Haiti Portal: https://launchpad.net/s3/haiti-quake-2010
- Haiti Offline: tbc
- Trunk: https://launchpad.net/s3/sahanapy
- RMS: https://code.launchpad.net/~uwthw/s3/rms
- VITA: https://code.launchpad.net/~nursix.org/s3/vita
Release Process
Overall responsible Person: Fran (currently)
- Where generic, new code is merged 1st into Trunk by Fran
- Code is then merged into the Haiti branch by Fran
- RMS is currently Haiti-specific so is merged directly to Haiti branch
- Code is pulled from the Haiti branch to Dev by Fran
- Data is 1st synced from Prod:
/root/automysqlbackup.sh scp /backups/daily/haitiprod/haitiprod_2010-01-27_01h24m.Wednesday.sql.gz flavour@haiti-dev.sahanafoundation.org:. gunzip haitiprod_2010-01-27_01h24m.Wednesday.sql.gz sed 's/haitiprod/haitidev/g' haitiprod_2010-01-27_01h24m.Wednesday.sql > haitidev_import.sql vim /etc/crontab #0-59/1 * * * * www-data cd /home/haiti/web2py/ && python web2py.py -C -D 1 >> /tmp/cron.output 2>&1 ln -sf /etc/apache2/sites-available/dev-maintain /etc/apache2/sites-enabled/dev /etc/init.d/apache2 force-reload mysql drop DATABASE haitidev; create DATABASE haitidev; \q mysql < haitidev_import.sql vim /home/haiti/dev/models/00_db.py migrate=True ln -sf /etc/apache2/sites-available/dev /etc/apache2/sites-enabled/dev /etc/init.d/apache2 force-reload vim /etc/crontab 0-59/1 * * * * www-data cd /home/haiti/web2py/ && python web2py.py -C -D 1 >> /tmp/cron.output 2>&1 vim /home/haiti/dev/models/00_db.py migrate=False
- A 1st draft of a data migration plan is made
- Data is 1st synced from Prod:
- Code is pulled from the Haiti branch to UAT by lifeeth or chamindra
- Testers to be informed before this via the group: tbc
- Data is 1st synced from Prod
- A more detailed data migration plan is made
- Code is pulled from the Haiti branch to Prod by Fran (Volunteers welcomed to take over!)
- Sign-off for this to come from the group: tbc
- A careful data migration plan is made
- Backup
- Disable system crontab & put Website into Maintenance Mode
vim /etc/crontab #0-59/1 * * * * www-data cd /home/haiti/web2py/ && python web2py.py -C -D 1 >> /tmp/cron.output 2>&1 vim /home/haiti/prod/models/00_db.py migrate=True rm /etc/apache2/sites-enabled/prod-ssl /etc/init.d/apache2 force-reload /home/haiti/update-prod /home/haiti/shell-prod db.export_to_csv_file(open('db.csv','wb')) Ctrl+D
- Disable system crontab & put Website into Maintenance Mode
- DataMigration
cd /home/haiti/web2py ; python web2py.py -S prod -M -N
Ctrl+D
- Restore Website & Cron
ln -s /etc/apache2/sites-available/prod-ssl /etc/apache2/sites-enabled /etc/init.d/apache2 force-reload vim /home/haiti/prod/models/00_db.py migrate=False vim /etc/crontab 0-59/1 * * * * www-data cd /home/haiti/web2py/ && python web2py.py -C -D 1 >> /tmp/cron.output 2>&1
Technical Details
Production
- haiti.sahanapy.org VM: 212.23.5.4
- /home/haiti/web2py/applications/prod
- /etc/apache2/sites-enabled/prod
SqliteMySQL- Web2Py r1544
- Sahana Haiti r547
- Customisations:
- models/00_db.py
#migrate = True migrate = False #exec('from applications.%s.modules.sahana import *' % request.application) # Faster for Production (where app-name won't change): from applications.prod.modules.sahana import * #exec('from applications.%s.modules.s3.s3validators import *' % request.application) # Faster for Production (where app-name won't change): from applications.prod.modules.validators import *
- models/00_settings.py
- auth.settings.registration_requires_verification = True
- auth.settings.registration_requires_approval = True
- models/01_modules.py
s3.menu_modules.append([T('Home'), False, URL(c='default', f='open_module', vars=dict(id=1))]) if auth.has_membership(1): s3.menu_modules.append([T('Admin'), False, URL(c='default', f='open_module', vars=dict(id=2))]) s3.menu_modules.append([T('Mapping'), False, URL(c='default', f='open_module', vars=dict(id=3))]) module_id = db(db.s3_module.name == 'or').select()[0].id s3.menu_modules.append([T('Organizations'), False, URL(c='default', f='open_module', vars=dict(id=module_id))]) s3.menu_modules.append([T('People'), False, URL(c='default', f='open_module', vars=dict(id=4))]) #dvi_group = db(db[auth.settings.table_group_name] == 'DVI').select()[0].id #if auth.has_membership(dvi_group): #module_id = db(db.s3_module.name == 'dvi').select()[0].id #s3.menu_modules.append([T('Victim Identification'), False, URL(c='default', f='open_module', vars=dict(id=module_id))]) module_id = db(db.s3_module.name == 'rms').select()[0].id s3.menu_modules.append([T('Requests'), False, URL(c='default', f='open_module', vars=dict(id=module_id))])
- cron/crontab
*/5 * * * * root *applications/prod/cron/rms_entry2record.py */5 * * * * root *applications/prod/cron/rms_tweet2request.py
- views/layout.html
<script type="text/javascript"> var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E")); </script> <script type="text/javascript"> try { var pageTracker = _gat._getTracker("UA-12510226-2"); pageTracker._setDomainName(".sahanafoundation.org"); pageTracker._trackPageview(); } catch(err) {}</script>
- views/ext.html
<!-- For Sites Hosted on the Public Internet, using Cachefly CDN's version of ExtJS may provide better performance <script type="text/javascript" src="/{{=request.application}}/static/scripts/ext/ext-all.js"></script> --> <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.0.3/ext-all.js"></script>
- views/sahana_scripts_min.html
<!-- For Sites Hosted on the Public Internet, using Google's version of jQuery will provide better performance <script src="/{{=request.application}}/static/scripts/web2py/jquery-1.3.2.min.js" type="text/javascript"></script> --> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
- views/gis/index.html
#LI( # SPAN(A(T('Bulk Uploader'), # _href=URL(c='gis', f='bulk_upload')),_style="font-weight:bold;"), # P(T('Allows authorized users to upload multiple features into the situation map.')), # _style="margin-top:1em;"),
- models/00_db.py
- http://haiti-test.sahanafoundation.org/test/gis/apikey/update/1
- ABQIAAAAgB-1pyZu7pKAZrMGv3nksRR1-chREdZE62d-MGawPNPOGidOJBQQCwHsElsSGbD5VMpNj7DBMNxjTg
UAT
- logistics.sahanapy.org VM: 212.23.5.5
- /home/haiti/web2py-test/applications/test
- /etc/apache2/sites-enabled/haiti-test
- MySQL hopefully
- Web2Py r1544
- Sahana Haiti r547
- Customisations:
- models/00_db.py
- migrate = False
- db = DAL('mysql://haititest:password@localhost/haititest', pool_size=10)
- models/00_settings.py
- S3_PUBLIC_URL = 'http://haiti-test.sahanafoundation.org'
- auth.settings.registration_requires_verification = True
- auth.settings.registration_requires_approval = True
- cron/crontab
*/5 * * * * root *applications/test/cron/rms_entry2record.py */5 * * * * root *applications/test/cron/rms_tweet2request.py
- views/ext.html
<!-- For Sites Hosted on the Public Internet, using Cachefly CDN's version of ExtJS may provide better performance <script type="text/javascript" src="/{{=request.application}}/static/scripts/ext/ext-all.js"></script> --> <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.0.3/ext-all.js"></script>
- views/sahana_scripts_min.html
<!-- For Sites Hosted on the Public Internet, using Google's version of jQuery will provide better performance <script src="/{{=request.application}}/static/scripts/web2py/jquery-1.3.2.min.js" type="text/javascript"></script> --> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
- views/gis/index.html
#LI( # SPAN(A(T('Bulk Uploader'), # _href=URL(c='gis', f='bulk_upload')),_style="font-weight:bold;"), # P(T('Allows authorized users to upload multiple features into the situation map.')), # _style="margin-top:1em;"),
- http://haiti-test.sahanafoundation.org/test/gis/apikey/update/1
- ABQIAAAAgB-1pyZu7pKAZrMGv3nksRR1-chREdZE62d-MGawPNPOGidOJBQQCwHsElsSGbD5VMpNj7DBMNxjTg
- models/00_db.py
Dev
- logistics.sahanapy.org VM: 212.23.5.5
- /home/haiti/web2py/applications/dev
- /etc/apache2/sites-available/dev
- /etc/apache2/sites-available/dev-maintain
- /etc/apache2/sites-enabled/dev
- MySQL
- Web2Py r1544
- Sahana Haiti r547
cd /home/haiti/web2py/applications bzr branch lp:s3/haiti-quake-2010 dev cd dev chown www-data:www-data -R cache chown www-data:www-data -R databases chown www-data:www-data -R errors chown www-data:www-data -R sessions chown www-data:www-data -R static/img/markers chown www-data:www-data -R static/scripts chown www-data:www-data -R static/styles chown www-data:www-data -R uploads
- Customisations:
- models/00_db.py
- migrate = False (after 1st run)
- db = DAL('mysql://haitidev:password@localhost/haitidev', pool_size=10)
- models/01_modules.py
s3.menu_modules.append([T('Home'), False, URL(c='default', f='open_module', vars=dict(id=1))]) if auth.has_membership(1): s3.menu_modules.append([T('Admin'), False, URL(c='default', f='open_module', vars=dict(id=2))]) s3.menu_modules.append([T('Mapping'), False, URL(c='default', f='open_module', vars=dict(id=3))]) module_id = db(db.s3_module.name == 'or').select()[0].id s3.menu_modules.append([T('Organizations'), False, URL(c='default', f='open_module', vars=dict(id=module_id))]) module_id = db(db.s3_module.name == 'hms').select()[0].id s3.menu_modules.append([T('Hospitals'), False, URL(c='default', f='open_module', vars=dict(id=module_id))]) s3.menu_modules.append([T('People'), False, URL(c='default', f='open_module', vars=dict(id=4))]) #dvi_group = db(db[auth.settings.table_group_name] == 'DVI').select()[0].id #if auth.has_membership(dvi_group): module_id = db(db.s3_module.name == 'dvi').select()[0].id s3.menu_modules.append([T('Victims'), False, URL(c='default', f='open_module', vars=dict(id=module_id))]) module_id = db(db.s3_module.name == 'rms').select()[0].id s3.menu_modules.append([T('Requests'), False, URL(c='default', f='open_module', vars=dict(id=module_id))]) module_id = db(db.s3_module.name == 'vol').select()[0].id s3.menu_modules.append([T('Volunteers'), False, URL(c='default', f='open_module', vars=dict(id=module_id))])
- models/00_settings.py
- S3_PUBLIC_URL = 'http://haiti-dev.sahanafoundation.org'
- auth.settings.registration_requires_verification = True
- auth.settings.registration_requires_approval = True
- cron/crontab
sed '/sahana/ s/sahana/dev/g' /home/haiti/dev/cron/crontab > /tmp/crontab mv /tmp/crontab /home/haiti/dev/cron/crontab vim cron/crontab */5 * * * * root *applications/dev/cron/rms_sms2record.py */5 * * * * root *applications/dev/cron/rms_tweet2request.py */5 * * * * root *applications/dev/cron/import_job_do_processing.py */5 * * * * root *applications/dev/cron/import_job_do_import.py
- views/ext.html
<!-- For Sites Hosted on the Public Internet, using Cachefly CDN's version of ExtJS may provide better performance <script type="text/javascript" src="/{{=request.application}}/static/scripts/ext/ext-all.js"></script> --> <script type="text/javascript" src="http://extjs.cachefly.net/ext-3.0.3/ext-all.js"></script>
- views/sahana_scripts_min.html
<!-- For Sites Hosted on the Public Internet, using Google's version of jQuery will provide better performance <script src="/{{=request.application}}/static/scripts/web2py/jquery-1.3.2.min.js" type="text/javascript"></script> --> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
- models/00_db.py
- http://haiti-dev.sahanafoundation.org/dev/gis/apikey/update/1
- ABQIAAAAgB-1pyZu7pKAZrMGv3nksRR1-chREdZE62d-MGawPNPOGidOJBQQCwHsElsSGbD5VMpNj7DBMNxjTg
- static/robots.txt
sed '/prod/ s/prod/dev/g' /home/haiti/dev/static/robots.txt > /tmp/robots.txt mv /tmp/robots.txt /home/haiti/dev/static/robots.txt
Note that UAT & Dev have different Web2Py instances so that:
- We can trial different Web2Py versions on Dev
- Crontab can be separately enabled/disabled
Note that all names/paths include the words 'dev'/'test'/'prod' to minimise chances of confusion as to which site is being altered