How to deploy code
THIS NEEDS TO BE UPDATED TO REFLECT THE NEW GIT/GERRIT WORKFLOW. -- I am now working on this, please don't touch the page in the meantime --Roan
Contents |
Basic common sense
- Be careful. Breaking the site is surprisingly easy!
- If you're deploying code written by someone else, ask them to be around during deployment so they can troubleshoot if necessary
- Make sure you know about anything hairy, such as additional prerequisites (e.g. schema changes) or potential complications when rolling back
Deployment requirements
- Cluster account request through RT; request deployment access (requires manager and/or sr dev approval)
- If you can ssh into fenari, and ssh into a random srv box (e.g. srv300) from there, you already have this.
- Deployment branch access requested by email to RobLa (CC Tim & Chad)
- Common sense. See above
- Some shiny code
Step 1: get the code in the deployment branch
Before you can deploy anything, it has to be in the deployment branch. Our deployment branches are named wmf/1.MAJORwmfMINOR where MAJOR and MINOR are numbers that increase over time as new branches are cut. A new branch with an incremented MINOR number is cut at the start of each deployment cycle, and after each tarball release MAJOR is incremented and MINOR is reset to 1. Strict access control is enforced on the deployment branches, but you should have access to them if you are a deployer. On the cluster, the checkout of each deployment branch is in /home/wikipedia/common/php-1.MAJORwmfMINOR .
Note that in most cases the cluster will be running on two deployment branches, with some wikis running version N and some running version N+1. To see which wiki is running which version, inspect /home/wikipedia/common/wikiversions.dat or look at Special:Version. If your code or change needs to go live to all wikis, you will need to change all deployment branches that are in use.
NOTE: All examples on this page assume there is a single deployment branch called wmf/1.20wmf1 checked out on the cluster in php-1.20wmf1. You may need to adapt the examples to use a different branch name. If you are updating multiple deployment branches, simply repeat the steps for each deployment branch separately.
NOTE: Also, all git examples assume you have a clean working copy, that is, you have no uncommitted changes. To verify this, run git status, it should say nothing added to commit (working directory clean) or nothing added to commit but untracked files present . If you are doing git-fu with a dirty working copy, there is a high probability you will screw things up, so don't do that unless you know what you're doing.
Case 1a: core changes
You are deploying changes to MediaWiki core. This should be rare because core is updated from master every two to three weeks, but in some cases it might be necessary. For core changes, you will simply need to push or submit changes to the wmf/1.20wmf1 branch in core. The most common use case is to take a commit that is already in the repository somewhere (usually in master, sometimes a commit that's still pending review) and cherry-pick it into the deployment branch, so only that case is documented below.
To cherry-pick a commit into the deployment branch, do the following things locally:
$ cd mediawiki/core # go to your checkout of mediawiki/core.git # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it from the remote $ git checkout wmf/1.20wmf1 $ git pull # Cherry-pick a commit from master, identified by its hash $ git cherry-pick ffb1b38ad83927606c539ac941e9f3eb2653a840 # If there are conflicts, this is how you fix them: # run 'git status' to see which files are conflicted # start fixing conflicted files # use 'git add filename' to tell git you've fixed the conflicts in a file # once all conflicts are resolved, commit the result using 'git commit' # Push the commit into the wmf/1.20wmf1 branch, bypassing review # If you do not have the rights to do this, or would like review of your change, # run 'git review' to submit it for review $ git push origin wmf/1.20wmf1
Case 1b: extension changes
You are deploying changes to an extension, but you don't just want to deploy master. Instead, you want to deploy the code that is currently deployed, plus your change. If you do want to just update your extension to master, read the extension update section instead.
Extensions have deployment branches too, but they are created on an as-needed basis, so your extension may or may not have a deployment branch with the correct name. If it doesn't exist yet, you'll have to create it. You can check by running git branch -a and checking if remotes/origin/wmf/1.20wmf1 or remotes/gerrit/wmf/1.20wmf1 appears in the list.
Creating an extension deployment branch
If your extension does not yet have a deployment branch, or doesn't have one with the correct name (e.g. there might be a 1.20wmf1 branch but used for an earlier deploy, but you're deploying to 1.20wmf3), you will need to create one. If your extension does have a correctly named deployment branch, you can skip this section.
First, find out the commit hash of the currently deployed commit:
$ cd mediawiki/core # Go to your checkout of core # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it $ git checkout wmf/1.20wmf1 $ git pull # Update the extension submodules. This may take a while when you run it for the first time $ git submodule update --init # cd to your extension's submodule $ cd extensions/MyCoolExtension # Look at the most recent commit $ git log -1 commit 434a740ef8dcd244029efd720a8e211e2803d343 # <---THIS Author: Reedy <reedy@wikimedia.org> Date: Wed Apr 11 14:45:57 2012 +0100
Special:Version also lists the deployed commit hash for each extension, in shorthand form (e.g. 434a740), you can use that to double-check.
Now, create a new branch based off that commit:
$ cd mediawiki/extensions/MyCoolExtension # Go to the extension checkout # Create a new branch and switch to it $ git checkout -b wmf/1.20wmf1 434a740ef8dcd244029efd720a8e211e2803d343 Switched to a new branch 'wmf/1.20wmf1' # Update .gitreview $ vim .gitreview # Change 'defaultbranch=master' to 'defaultbranch=wmf/1.20wmf1' $ git commit .gitreview -m 'Create wmf/1.20wmf1 branch and update .gitreview' # Push the branch into gerrit $ git push origin wmf/1.20wmf1 # Let the local branch track the remote branch $ git branch --set-upstream wmf/1.20wmf1 origin/wmf/1.20wmf1
Now that your extension has a deployment branch
Just like in core, the most common use case for updating a deployment branch is to cherry-pick changes from master:
$ cd mediawiki/extensions/MyCoolExtension # go to your extension checkout # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it from the remote $ git checkout wmf/1.20wmf1 $ git pull # Cherry-pick a commit from master, identified by its hash $ git cherry-pick 176ffdd3b71e463d3ebaa881a6e77b82acba635d # If there are conflicts, this is how you fix them: # run 'git status' to see which files are conflicted # start fixing conflicted files # use 'git add filename' to tell git you've fixed the conflicts in a file # once all conflicts are resolved, commit the result using 'git commit' # Push the commit into the wmf/1.20wmf1 branch, bypassing review # If you do not have the rights to do this, or would like review of your change, # run 'git review' to submit it for review $ git push origin wmf/1.20wmf1
Additionally, however, you will need to update the submodule in core:
$ cd mediawiki/core # Go to your checkout of core # Set up a local wmf/1.20wmf1 branch that tracks the remote # You only need to do this once; if you've already got a wmf/1.20wmf1 branch, you can skip this step $ git branch --track wmf/1.20wmf1 origin/wmf/1.20wmf1 Branch wmf/1.20wmf1 set up to track remote branch wmf/1.20wmf1 from origin. # Switch to the wmf/1.20wmf1 branch and update it $ git checkout wmf/1.20wmf1 $ git pull # Update the extension submodules. This may take a while when you run it for the first time $ git submodule update --init # cd to your extension's submodule $ cd extensions/MyCoolExtension # Update it from the remote $ git fetch # Point the submodule to the remote wmf/1.20wmf1 branch $ git checkout origin/wmf/1.20wmf1 HEAD is now at 96052d0... (bug 34885) Blah blah blah # cd back to the main repo $ cd ../.. # Check that you have updated the submodule correctly # The diff should only show your submodule change, nothing else $ git diff diff --git a/extensions/MyCoolExtension b/extensions/MyCoolExtension index 6a6eaab..96052d0 160000 --- a/extensions/MyCoolExtension +++ b/extensions/MyCoolExtension @@ -1 +1 @@ -Subproject commit 6a6eaabcdae29201c0f3e7bbcbac2d1953c574a5 +Subproject commit 96052d0d9733f89d5f8fd43c6ba05ecc756ea1ec # Commit the submodule update and push it $ git commit -a -m "Update MyCoolExtension" $ git push origin wmf/1.20wmf1
Case 1c: extension update
Case 1d: new extension
Add new extensions to make-wmf-branch/default.conf
If you added a new extension to the deployment branch, you should also add it to /trunk/tools/make-wmf-branch/default.conf in SVN (in the $normalExtensions array) so it'll be picked up when the deployment branch is rebranched.
Step 2: get the code on fenari
Once the code is in the deployment branch, you simply run svn up on fenari to get it there:
catrope@fenari:~$ cd /home/wikipedia/common/php/ catrope@fenari:/home/wikipedia/common/php/$ svn up
The code is now automatically running on test.wikipedia.org as well, and can be tested there. If, however, you have svn up'd Javscript/CSS files that do not get loaded with ResourceLoader, you will need to ssh into srv193 (the server that runs testwiki) and run sync-common before they are live on testwiki.
Step 3: configuration and other prep work
In certain cases, you'll have to change how Wikimedia sites are configured. We generally have the same codebase everywhere, but with different configurations for each wiki.
Maybe you are just changing one configuration variable. Or, perhaps you are adding a brand-new extension, or activating an extension on some wiki where it's never been before. For all of these cases, and more, you'll have to make the changes to the config files to get the desired results.
Configuration files live in their own revision-controlled repository. Everything that follows is just a convenient way to make those changes.
If you're deploying an extension or feature that can be switched off, it's usually best to leave it switched off while you deploy and carefully switch it on after that using a simple configuration change (this is called a dark launch). Even if you do this, you should build any configuration infrastructure (e.g. $wmg variable, adding entry in InitialiseSettings with default false) at this time so all you'll have to do later is flip a switch.
For specific preparations, see the sections below as well as How to do a schema change and How to do a configuration change. Best to perform schema changes before making config changes.
Add a configuration switch for an extension
In /home/wikipedia/common/wmf-config/CommonSettings.php, add:
if ( $wmgEnableMyExtension ) { require_once( "$IP/extensions/MyExtension/MyExtension.php" ); // Set config vars if needed // If you want to export config vars through InitialiseSettings.php, you need to set $wmgMyExtensionThingy there and do #$wgMyExtensionThingy = $wmgMyExtensionThingy; }
In /home/wikipedia/common/wmf-config/InitialiseSettings.php, add something like:
'wmgEnableMyExtension' => array( 'default' => false, 'eswikibooks' => true, // etc. ), // If needed, set $wmgMyExtensionWhatever vars here too
If your extension requires a large-ish amount of configuration, consider putting it in a separate file instead. Currently, AbuseFilter, LiquidThreads and FlaggedRevs do this.
For more documentation on these files and their formats, see Configuration files.
Add new extensions to extension-list
When adding a new extension, you need to add it to the extension list, or its i18n messages won't get picked up. For more information about this setup, see this.
- cd to
/home/wikipedia/common/wmf-config - Edit
extension-listand add the path to the extension setup file on a line by itself - Where X.XX is the current MediaWiki version, run
mwscript ../php/maintenance/mergeMessageFileList.php --wiki=aawiki --list-file=extension-list --output=ExtensionMessages-X.XX.phpand watch the output for errors. Your extension should appear in the output the same way the others do- If you get an error, you probably got the path wrong, or the extension setup file is wonky
- If you get an error, you can also try running
/home/catrope/sync-common, then runningmergeMessageFileList.phpagain like above
- Run
svn diffand verify that the changes toExtensionMessages-X.XX.phpmake sense- A line like
'myext' => "$IP/extensions/myext/myext.i18n.php",should be added, and there should be no unrelated changes. Check the path to the i18n file for correctness
- A line like
- Do what needs to be done to deploy and register changes in these config files.
Step 4: synchronize the changes to the cluster
Small changes: sync individual files
If your change only touches one or a few files, you can sync them individually with sync-file rather than having to run scap. This is preferable because a scap run always shakes the cluster up a bit and takes longer to complete, while a sync-file run is very lightweight. However, sync-file is only capable of synchronizing files wihtin directories that already exist on the cluster, so it won't work with newly added directories. Also, sync-file only synchronizes one file at a time, and creates a log entry each time. Using it repetitively (e.g. with a for loop) to sync multiple files is fine, as long as there's not too many of them (say not more than ~5).
To sync a single file, run sync-file [path to file] [summary]. The summary will be used by logmsgbot to log your sync in #wikimedia-tech, from where it'll go to the server admin log and the identi.ca and Twitter feeds. Typically, you would use the revision ID
- PITFALL: The path argument has to be relative to the
commondirectory, not to the current directory. To preserve your sanity (and tab-completion functionality), I recommend you always cd tocommonbefore runningsync-file - PITFALL: If the summary argument contains spaces, you'll have to put it in quotes or only the first word is used. If your summary contains a
$, you'll either have to escape it or put your summary in single quotes, to prevent bash's variable expansion from messing it up
When running sync-file, you'll usually see about half a dozen errors from broken servers. We should really fix things some time so this doesn't happen, but in the meantime you can consider this completely normal (sample output below). It usually completes within a few seconds, but in rare cases it may hang on a broken Apache for 1 or 2 minutes.
catrope@fenari:/home/wikipedia/common$ sync-file php/api.php r12345 No syntax errors detected in /home/wikipedia/common/php-1.5/api.php copying to apaches srv104: ssh: connect to host srv104 port 22: No route to host srv125: ssh: connect to host srv125 port 22: No route to host srv149: ssh: connect to host srv149 port 22: Connection refused srv169: ssh: connect to host srv169 port 22: Connection refused srv284: ssh: connect to host srv284 port 22: No route to host srv145: ssh: connect to host srv145 port 22: Connection timed out srv206: ssh: connect to host srv206 port 22: Connection timed out srv266: ssh: connect to host srv266 port 22: Connection timed out
More complex changes: sync everything
If you're adding directories, changing many files, or otherwise have a reason why sync-file wouldn't work or would be impractical, you'll have to run scap, which syncs everything and rebuilds caches. scap logs to the server admin log with the revision number you updated to, and reports in #wikimedia-tech (without !log) when it finishes.
awjrichards@fenari:/home/wikipedia/common/php$ scap 'Log message here' Checking syntax... Compiling texvc...ok Copying to fenari...Done. Updating serialized data files... Warning: messages are no longer serialized by this makefile. Updating ExtensionMessages.php... ...snip...
Running scap takes a few minutes. There is usually a load spike and a few hiccups on the cluster immediately after scapping, but that's normal, as long as it doesn't last longer than a few minutes.
Test your code live
Is it doing what you expected? Unfortunately, testwiki is not exactly like the real wikis. Consider that extensions can activate hooks. Things like CentralNotice or Common.js might change the JS environment too. No one environment can simulate all the wikis that we operate.
Don't leave town
Even if your deploy appears to be working, it's important to be reachable in the hours immediately following your deploy. Ideally, stay online and in IRC channels like #wikimedia-tech for a couple of hours.
If you must go offline, let people know how to reach you (and keep your mobile phone or other communications device on your person). You can use /away messages on IRC, or perhaps send a short email to private-l.
If you are on Wikimedia staff, now might be a great time to check if your contact info is up to date. If you aren't on staff, ask a staffer to add your contact info to that page, under "Important volunteers".
A note on JavaScript and CSS
Since we now have ResourceLoader, there is no need to do anything to re-minify and re-cache JavaScript and CSS. However, it may take up to five minutes before that occurs.