]> scripts.mit.edu Git - wizard.git/blob - doc/repository-conversion.rst
Expand documentation.
[wizard.git] / doc / repository-conversion.rst
1 Repository conversion
2 =====================
3
4 One of Wizard's goals is to replace the previous autoinstaller infrastructure.
5 Pre-wizard autoinstalls live in :file:`/mit/scripts/deploy` and consist of a
6 tarball from upstream, possibly a scripts patch, and possibly some post-install
7 munging (such as the creation of a :file:`php.ini` file and appropriate
8 symlinks).
9
10 Conversion to use Wizard involves placing :term:`pristine` versions of the source
11 code (from the upstream tarballs) and appropriately patched scripts
12 versions into a Git repository, as well as writing a :mod:`wizard.app`
13 module for the application that implements application specific logic, such
14 as how to install, upgrade or backup the installation.
15
16 Here is a tutorial for performing a conversion, using Wordpress as
17 an example.
18
19 Setup
20 -----
21
22 .. highlight:: sh
23
24 Probably the easiest way to do development is entirely on :term:`AFS`:  all
25 of your source code should live in publically readable (i.e.
26 ``system:anyuser`` as read permissions) directories, so that if you
27 SSH into a scripts server to perform testing, you will be able
28 to invoke your tools and read your development repository.  In order
29 to be able to run the test scripts in the tests directory, this
30 is preferably in :file:`web_scripts`. In that
31 case, setup is as simple as::
32
33     git clone /mit/scripts/git/wizard.git /mit/$USER/web_scripts/wizard
34     athrun consult fsr /mit/$USER/web_scripts/wizard system:anyuser read
35     # for any application you're going to do development on, also:
36     git clone /mit/scripts/git/autoinstalls/$APP.git /mit/$USER/web_scripts/wizard/srv/$APP
37
38 If you'd like to be able to develop offline, just note that you will
39 have to push your changes to AFS once you start doing testing on
40 scripts servers, but before your changes get incorporated into
41 canonical upstream.  Git doesn't exactly make this easy, but you
42 can follow this recipe::
43
44     git clone /mit/scripts/git/wizard.git ~/wizard
45     cd /mit/$USER
46     mkdir wizard.git
47     cd wizard.git
48     git init --bare
49     cd ~/wizard
50     git remote add afs /mit/$USER/wizard.git
51     git push -f afs master
52     git clone /mit/$USER/wizard.git /mit/$USER/wizard
53
54 And then you can perform updates from your local copy with::
55
56     git push afs master
57     cd /mit/$USER/wizard
58     git pull
59
60 If :file:`/mit/$USER/wizard.git` has write permissions for
61 ``system:scripts-security-upd``, this is especially useful if you were hacking
62 on a copy living on ``not-backward.mit.edu``, and now need to transfer the
63 changes back to the canonical repository (please don't give ``not-backward.mit.edu``
64 your root tickets!)  You can also setup a wizard directory similar to the
65 first set of directions for on-server testing.
66
67 From this point on, we will assume you are doing development from an AFS directory
68 named ``$WIZARD``; note that application repositories live in ``$WIZARD/srv``.
69
70 Pristine
71 --------
72
73 .. highlight:: sh
74
75 This is a tutorial centered around migrating `Wordpress <http://wordpress.org/>`_
76 into our Git repository.  For the sake of demonstration,
77 we shall assume that this repository hasn't been created yet.
78 The repository then doesn't exist, we should create it::
79
80     cd "$WIZARD/srv"
81     mkdir wordpress
82     cd wordpress
83     git init
84
85 .. highlight:: python
86
87 We also have to create a module for the application, so we
88 create ``$WIZARD/wizard/app/wordpress.py`` and fill it in with a bare bones template::
89
90     from wizard import app
91     class Application(app.Application):
92         pass
93
94 .. highlight:: sh
95
96 Now we are ready to put some code in our repository: the first thing we will
97 add is the :term:`pristine` branch, which contains verbatim the code from upstream.
98 If we were starting a new autoinstaller, we'd pop off and use the latest version,
99 but since we're dealing with legacy we want to start our repository history
100 with the **oldest** version still extant on our servers.  To find this out run::
101
102     wizard summary version APP
103
104 You'll need to be in the ``scripts-team`` list in order to access the data to
105 run this command.
106
107 Try running the following command in :file:`$WIZARD/srv/wordpress`::
108
109     wizard prepare-pristine wordpress-2.0.2
110
111 .. highlight:: python
112
113 You should get an error complaining about :meth:`wizard.app.Application.download`
114 not being implemented yet. Let's fix that::
115
116     class Application(app.Application):
117         def download(self, version):
118             return "http://wordpress.org/wordpress-%s.tar.gz" % version
119
120 .. highlight:: sh
121
122 We determined this by finding `Wordpress's Release Archive <http://wordpress.org/download/release-archive/>`_
123 and inferring the naming scheme by inspecting various links.  You should now
124 be able to run the prepare-pristine command successfully: when it is
125 done, you'll now have a bunch of files in your repository, and they
126 will be ready to be committed.  Inspect the files and commit (note that the
127 format of the commit message is a plain Appname Version.Number)::
128
129     git status
130     git commit -asm "Wordpress 2.0.2"
131     git tag wordpress-2.0.2
132
133 .. note::
134
135     Sometimes, ``http://wordpress.org/wordpress-2.0.2.tar.gz`` won't
136     actually exist anymore (it didn't exist when we did it).  In this case,
137     you'll probably be able to find the original tarball in
138     :file:`/mit/scripts/deploy/wordpress-2.0.2`, and you can feed it
139     manually to prepare pristine with
140     ``wizard prepare-pristine /mit/scripts/deploy/wordpress-2.0.2/wordpress-2.0.2.tar.gz``
141
142 Some last house-keeping bits:  now that you have a commit in a repository, you
143 can also create a pristine branch::
144
145     git branch pristine
146
147 From the point of view of the pristine branch pointer, history should be a
148 straight-forward progression of versions.  Once you have more versions,
149 it will look something like:
150
151 .. digraph:: pristine_dag
152
153     rankdir=LR
154     node [shape=square]
155     subgraph cluster_pristine {
156         a -> b -> c
157         a [label="1.0"]
158         b [label="1.1"]
159         c [label="2.0"]
160         label = "pristine"
161         color = white
162         labeljust = r
163     }
164
165 Scriptsify
166 ----------
167
168 In a perfect world, the pristine version would be equivalent to the scriptsified
169 version that would actually get deployed.  However, we have historically needed
170 to apply patches and add extra configuration files to get applications to
171 work correctly.  Due to the way Git's merge algorithm works, the closer we are
172 able to reconstruct a version of the application that was actually used, the
173 better off we will be when we try to subsequently upgrade those applications.
174
175 To give you an idea of what the Git history graph will look like when we
176 are done, here is the graph from before, but augmented with the scripts versions:
177
178 .. digraph:: master_dag
179
180     rankdir=LR
181     node [shape=square]
182     subgraph cluster_pristine {
183         a -> b -> c
184         a [label="1.0"]
185         b [label="1.1"]
186         c [label="2.0"]
187         label = "pristine"
188         color = white
189         labeljust = r
190     }
191     subgraph cluster_master {
192         as -> bs -> cs
193         as [label="1.0-scripts"]
194         bs [label="1.1-scripts"]
195         cs [label="2.0-scripts"]
196         label = "master"
197         color = white
198         labeljust = r
199     }
200     a -> as
201     b -> bs
202     c -> cs
203     a []
204
205 .. highlight:: sh
206
207 First things first: verify that we are on the master branch::
208
209     git checkout master
210
211 Check for pre-existing patches in the old application directory,
212 :file:`/mit/scripts/deploy/wordpress-2.0.2` in the case of Wordpress,
213 and apply them::
214
215     patch -n0 < /mit/scripts/deploy/wordpress-2.0.2/wordpress.patch
216
217 Then, run the following command to setup a :file:`.scripts` directory::
218
219     wizard prepare-new
220
221 This directory holds Wizard related files, and is also used by
222 :command:`parallel-find.pl` to determine if a directory is an autoinstall.
223
224 Finally, if you are running a PHP application, you'll need to setup
225 a :file:`php.ini` and symlinks to it in all subdirectories::
226
227     cp /mit/scripts/deploy/php.ini/wordpress php.ini
228     athrun scripts fix-php-ini
229
230 .. note::
231
232     As of November 2009, all PHP applications load the same :file:`php.ini` file.
233
234 Now commit, but don't get too attached to your commit; we're going
235 to be heavily modifying it soon::
236
237     git commit -asm "Wordpress 2.0.2-scripts"
238
239 Installation
240 ------------
241
242 We now need to make it possible for a user to install the application.
243 Most web applications have a number of web scripts for generating a
244 configuration file, so creating the install script involves:
245
246     1. Determining what input values you will need from the user, such
247        as a title for the new application or database credentials; more
248        on this shortly.
249
250     2. Determining what POST values need to be sent to what URLs.
251        Since you're converting a repository, this job is even simpler: you just
252        need to port the Perl script that was originally used into Python.
253
254 There's an in-depth explanation of named input values in
255 :mod:`wizard.install`.  The short version is that your application
256 contains a class-wide :data:`~wizard.app.Application.install_schema`
257 attribute that encodes this information.  The constructor takes
258 an arbitrary number of arguments (to be continued)
259
260 Some tips and tricks for writing :meth:`wizard.app.Application.install`:
261
262     * Some configuration file generators will get unhappy if the
263       target directory is not chmod'ed to be writable; dropping
264       in a ``os.chmod(dir, 0777)`` and then undoing the chmod
265       when you're done is a decent workaround.
266
267     * :func:`wizard.install.fetch` is the standard workhorse for making
268       requests to applications.  It accepts three parameters; the first
269       is ``options`` (which was the third argument to ``install`` itself),
270       the second is the page to query, relative to the installation's
271       web root, and ``post`` is a dictionary of keys to values to POST.
272
273     * You should log any web page output using :func:`logging.debug`.
274
275     * If you need to manually manipulate the database afterwards, you
276       can use :func:`wizard.sql.mysql_connect` (passing it ``options``)
277       to get a `SQLAlchemy metadata object
278       <http://www.sqlalchemy.org/docs/05/sqlexpression.html>`_, which can
279       consequently be queried.  For convenience, we've bound metadata
280       to the connection, you can perform implicit execution.
281
282 To test if your installation function works, it's probably convenient to
283 create a test script in :file:`tests`; :file:`tests/test-install-wordpress.sh`
284 in the case of Wordpress.  It will look something like::
285
286     #!/bin/bash -e
287
288     DEFAULT_HEAD=1
289     TESTNAME="install_wordpress"
290     source ./setup
291
292     wizard install "wordpress-$VERSION-scripts" "$TESTDIR" --non-interactive -- --title="My Blog"
293
294 ``DEFAULT_HEAD=1`` indicates that this script can perform a reasonable
295 operation without any version specified (since we haven't tagged any of our
296 commits yet, we can't use the specific version functionality; not that we'd want
297 to, though).  ``TESTNAME`` is simply the name of the file with the leading
298 ``test-`` stripped and dashes converted to underscores.
299
300 (to be continued)