I have been mulling a long time how I am going to keep track of SugarCRM, the custom modules I build and and changes to the core code that I need to make. The latter is sometimes unavoidable because some functionality cannot be built any other way. Also, sometimes you run into a bug that you need to fix, but you cannot wait for the next Sugar release. Of course, all of this needs to be done in a way that is easy for developers to work with. I think I have finally found a way that is workable and keeps everything under version control.
I will present my workflow in two articles. This article shows you how I keep SugarCRM itself under version control, how I deal with deployment and how I do upgrades in the face of changes to the core code. In “Build custom SugarCRM modules in Subversion” I will present how my custom modules—which are also under version control—fit into this system. Hat-tip to Leonid Mamchenkov for his work and insights.
0: Table of contents
- 1: Notes on this setup
- 2: Setting up a development server
- 3: Set up Subversion and prepare for importing Sugar
- 4: Import Sugar into Subversion
- 5: Import a new release
- 6: Update the trunk and working copies to a new release
- 7: Deploying and updating live installations
- 8: Footnotes
1: Notes on this setup
The setup I am describing below allows me to keep SugarCRM under control when I make changes to the core code. As I explained above, sometimes you simply have to change the core code. Either to fix a bug that upstream does not fix fast enough or because a feature cannot be implemented in an upgrade safe way. There is however a fundamental problem with keeping any web application under version control: You can version control the source code, but not the database.
The way this dilemma is usually solved is by making the database follow the source code. The source is always the master and the database always the slave. When the source code and the database disagree then the source is leading and the database adapts. Ideally this happens automatically. With SugarCRM this is a problem however. Custom fields created in Studio for example are stored in the database, not in vardef files, but those custom fields are referenced in e.g. the viewdef files. Now you have a circular dependency between the source code and the database and as any programmer can tell you, circular dependencies are evil.
I break this circle by forcing the source code to be leading. Luckily SugarCRM has a very useful function called “Quick rebuild and repair” that can detect changes in the vardefs and make the database follow it. This does however mean that all such changes need to happen in the vardefs to begin with. This boild down to: “Don’t use Studio”. If you use the below setup while you manage custom fields, layouts and relationship with Studio then you will run into conflicts*.
I manage all my custom fields, layouts and relationships with a custom package that I can install through the module loader. This package is also kept under Subversion control but sits in a separate repository. “Build custom SugarCRM modules in Subversion” explains how this works exactly. The base SugarCRM is what I keep under version control with the method shown below. The only updates made to it are my changes and fixes to the core code, and updated releases from SugarCRM upstream. Nothing else.
2: Setting up a development server
For my system I use a separate development server that has been configured in such a way that I can quickly make a subversion checkout and have a new copy Sugar running. No Apache configuration required because it’s all automated away.
Take a look at my article “Easily develop and deploy web applications from subversion” to see how I have set up Apache. What it boils down to is that I can make a Subversion checkout in a predefined directory, and Apache automatically picks up that subdirectory as a separate subdomain. So, if I do this:
- svn checkout https://svn.example.org/sugar/trunk /var/checkouts/my-checkout
Apache will see the “my-checkout” directory and (thanks to wildcard DNS) make it available on http://my-checkout.dev.example.org automatically. I did a similar trick for the MySQL database because I don’t want to manually grant database rights every time I create a new SugarCRM checkout. This is easy to do because MySQL supports wildcards in the database name when you grant right. So, manually I created a new database called sugar-vendor-510b (because I will be installing Sugar 5.1.0b today), created a new MySQL user called sugar and then I granted him rights on the database sugar-*. This way the new user will have rights to any database whose name starts with “sugar-”.
3: Set up Subversion and prepare for importing Sugar
Now it is time to import the first SugarCRM. I assume that you have an empty Subversion repository at http://svn.example.org/sugar to which you have rights. If you need help setting up Subversion under Apache, check out my older article “Apache and Subversion authentication with Microsoft Active Directory”. Start by creating a standard Subversion layout, but notice I do not create trunk/ yet.
- mkdir -p temp/branches temp/tags temp/vendors/vendors-510b
- svn import temp http://svn.example.org/sugar -m "Creating repository layout"
Now go to your Apache checkout directory, make a checkout of the vendor branch, unzip the Sugar tarball and put the files in the checked out directory.
- cd /var/checkouts
- svn checkout http://svn.example.org/sugar/vendors/sugar-510b
- wget http://www.sugarforge.org/frs/download.php/4742/SugarCE-5.1.0b.zip
- unzip SugarCE-5.1.0b.zip
- mv SugarCE-Full-5.1.0b/* sugar-510b/
- mv SugarCE-Full-5.1.0b/.htaccess sugar-510b/
- rmdir SugarCE-Full-5.1.0b
Before you can install Sugar you need to make sure that it can write to the directories. When you upgrade Sugar it wants to be able to write to all the files, so give it full access.
- sudo chgrp -R www-data sugar-510b
- chmod -R g+w sugar-510b
Now you can go to to http://sugar-510b.dev.example.org/install.php and install Sugar using the webinterface. Use the database and MySQL user that you created earlier. After installation we need to edit a few files before we can perform the actual import. SugarCRM stores the name of the database and the full URL of your sugar installation in config.php. We need to abstract these so that we can easily change these without having to edit config.php itself. I create two files name .hthost and .htdatabase that contain the hostname and database name respectively. Then I change the config.php script so that these variables are read from the file.
- cd sugar-510b
- echo "sugar-510b.dev.example.org" > .hthost
- echo "sugar-vendor-510b" > .htdatabase
In config.php
- <?php
- $sugar_config = array (
- ...
- 'dbconfig' =>
- array (
- 'db_host_name' => 'localhost',
- 'db_host_instance' => 'SQLEXPRESS',
- 'db_user_name' => 'sugar5',
- 'db_password' => '<password>',
- 'db_name' => trim(file_get_contents('.htdatabase')),
- 'db_type' => 'mysql',
- ),
- ...
- 'host_name' => trim(file_get_contents('.hthost')),
- ...
- );
- ?>
You can even automate it further, but then you will need to change the config file manually when you deploy to a different server. Notice that the database name is now “sugar-510b”.
- 'db_name' => basename(dirname(__FILE__)),
- ...
- 'host_name' => basename(dirname(__FILE__)) . '.dev.example.org',
Next we need to add a few directories to the cache/ directory. The install script does not automatically create these but we do want to keep track of them (so we can ignore their contents later on). Please read my previous article The SugarCRM cache directory demystified to find out why you can ignore the contents of these directories.
- mkdir cache/blowfish cache/diagnostic cache/dashlets
The last thing to do is to rename the sugar-vendor-510b database. I do this so that I don’t accidentally write to the database anymore. I renamed it to vendor-sugar-510b so that it does not start with sugar- anymore and the sugar MySQL user does not have any rights to it.
4: Import Sugar into Subversion
Now you can import Sugar into Subversion. Start off by setting some ignore properties on the root directory. Note that to make it multiline you press Ctrl+Return, not just Return!
- svn propset svn:ignore "install.log
- sugarcrm.log
- .hthost
- .htdatabase" .
Next, add the cache directory, its subdirectories and set ignores on those as well.
- svn add --depth=empty cache
- cd cache/
- svn add --depth=empty blowfish csv dashlets diagnostic feeds generated_forms images import jsLanguage layout modules pdf smarty upload xml
- svn add csv/index.html feeds/index.html images/index.html import/index.html layout/index.html pdf/index.html upload/index.html xml/index.html
- svn propset svn:ignore '*' blowfish csv dashlets diagnostic feeds generated_forms images import jsLanguage layout modules pdf smarty upload xml
Now we can add everything that is not ignored and commit it
- cd ..
- svn add --force
- svn commit -m "Importing sugar-510b vendor drop"
The last thing to do is to copy this vendor branch to the trunk and copy the vendor-sugar-510b database to sugar-trunk.
- svn copy http://svn.example.org/sugar/vendors/sugar-510b \
- http://svn.example.org/sugar/trunk \
- -m "Copying sugar-510b vendor branch to trunk"
Check out a working copy
Making a checkout is pretty easy in this setup. First I make a copy of the vendor database. I copied vendor-sugar-510b to sugar-test. Then I make a checkout using the following commands.
- cd /var/checkouts
- svn checkout https://svn.example.org/sugar/trunk test
- sudo chgrp -R www-data test
- chmod -R g+w test
- echo "sugar-test" > test/.htdatabase
- echo "test.dev.example.org" > test/.htdatabase
Now I can go to http://test.dev.example.org and log into my working copy of SugarCRM.
5: Import a new release
Importing a new release into Subversion is quite easy. First I copy the vendor branch to a new branch that will get the update.
- svn copy http://svn.example.org/sugar/vendors/sugar-510b \
- http://svn.example.org/sugar/vendors/subar-510c \
- -m "Copying vendor branch in preparation for update to 510c"
I copy the vendor-sugar-510b database to sugar-vendor-510c, make a checkout of the new vendors/sugar-510c in my checkouts directory and then I install the update package from SugarCRM on that installation. After the update completes I commit vendors/sugar-510c, but do check the config.php file before you do so. SugarCRM has a tendency to regenerate its config file and when it does, you need to re-apply the changes that load the database and hostname from .htdatabase and .hthost.
Finally rename the database to vendor-sugar-510c to protect it against accidental writes. I now have a clean copy of both the 510b source code and database as well as the 510b and 510c database.
6: Update the trunk and working copies to a new release
Updating the trunk or any other working copy is slightly more work, because it is a two-step process. You don't know what kind of changes the update will make to your database so you need to actually install the update. You cannot simply update the source code and run “Quick rebuild and repair”. Sometimes SugarCRM makes very invasive changes to your database that are not reflected in the vardefs. For example, the update from Sugar 5.0.0a to 5.0.0b encrypted the SMTP password field in the database. But installing an update overwrites the source code files so you will loose any changes that you made to core files. The solution is to update the source code and database separately.
6.1: Update the database
If you want to update a working copy then you will need to commit your changes. For safety you may want o backup your database. Now make another checkout (or export) of that branch or trunk to a temporary directory. For example, make a checkout of the trunk to /var/checkouts/temp. Configure this checkout to user the sugar-trunk database (or whatever database you want to upgrade). Now go to this temporary SugarCRM and install the update in the regular way. When the update tells you that it has found local changes, tell it to overwrite all files with the new version. When the update is complete then your database has been upgraded. You can remove the temporary checkout directory. Note: Do not commit this temporary checkout because all your custom changes have been undone!
6.2: Update the source code
Updating the source code is done using a regular Subversion merge so that any modifications we have made will remain intact. Remember that we copied vendors/sugar-510b to vendors/sugar-510c? We can use this to generate the exact changeset between the two versions. Below you can see how I would update the source code of my checkout of the trunk to the new Sugar version.
- svn merge http://svn.example.org/sugar/vendors/sugar-510b \
- http://svn.example.org/sugar/vendors/sugar-510c \
- /var/checkouts/trunk
Resolve any conflicts between the SugarCRM update and your own changes and commit.
- svn commit -m "Updating to SugarCRM 5.1.0c"
Other people can now simply run “svn update” on their working copies of the trunk. Now you have updated both the database and the source code so you can use Sugar again. The only tricky part about this bit is that the database and the source code should be updated at the same time. If you are working with multiple developers who use their own working copies of the trunk but share a common sugar-trunk database then one developer needs to make this update. The other developers cannot use Sugar in the period in between the database update and the source code update.
7: Deploying and updating live installations
Deploying a live installation is simple. It is exactly the same as checking out a working copy. The only difference is that this time you probably need to configure Apache because most likely you will not be running the live copy out of your /var/checkouts directory.
Updating a live installation is also pretty much the same. First update the database and then update the source code. If there have been no new SugarCRM updates then there is no need to update the database. If there were then you need to check out an appropriate working copy from your repository and use that to update the database by installing the SugarCRM update. It works the same way as updating a working copy because a live deployment is in essence just another working copy.
After the database has been updated you can run “svn update“ followed by a “Quick rebuild and repair” to update the codebase and propagate the last changes into the database layout.
8: Footnotes
…conflicts. Such circular dependencies are one of the many ways in which SugarCRM is hostile towards maintenance under Subversion. Another one is its tendency to simply delete and recreate some cache directories during upgrades, after which you lose your .svn directories and Subversion will start complaining. I have no idea what revision control system is used by the SugarCRM developers—if they use any at all—but I would love to know how they handle these kinds of issues. Unfortunately the development process of SugarCRM is just as closed as that of Microsoft or Apple (something I commented on earlier) so we may never know.
Comments
#1 SugarDev.net (http://sugardev.net)
#2 Sander Marechal (http://www.jejik.com)
Oh well, yet another nonsensical inconsistency on the list... :-/
#3 SugarDev.net (http://sugardev.net)
In 4.2.1, I know for sure that config_override.php wouldn't be completely overwritten...seems somewhere along the way someone got nervous..
#4 Loek van Gool (http://madcap.nl)
1. Changes to the code (beautifully managed with your svn merge)
2. Changes to the database (need to be ran using the Sugar upgrader, as you cannot extract the queries themselves (actually you can, but Sugar includes all queries, even skipped ones, so that you cannot use this as a database upgrade script))
3. Post/pre install scripts (like moving all emailaddresses to their own tables while upgrading to Sugar 5)
For running installs, 2 and 3 can cause problems which are unsolvable using svn merge to upgrade Sugar. Sugar lacks a way of knowing what actions need to be taken in order to complete an upgrade in case the code is upgraded while the database hasn't. It either happens while upgrading using the SugarCRM Upgrade Wizard, or it never happens. I don't think upgrading can be done this way safely. This is unfortunate, it is so nice in theory.
#5 Sander Marechal (http://www.jejik.com)
Then upgrade the real source code from Subversion using an `svn update`. That code still has your local changes thanks to Subversion's merging.
That mostly solves your #2 and #3. The only downside is that the source and the database must be updated at the same time. As you said, it's not possible to update one but not the other. I recommend that you create a new Subversion branch every time you upgrade your database. Always use a database with the correct source code branch. This way you also won't accidentally `svn update` your source code without doing a database change, because you would need to explicitly `svn switch` to the new branch.
It's not perfect, but it works.
#6 Damien
The only issue I have is with:
$ svn add --force
I had to use *:
$ svn add * --force
#7 dasho
I have studied your tutorials about how to manage a SugarCRM project with Subversion and they are really helpful. Me too have
tried to manage SugarCRM projects with Subversion and I have
encountered most of the problems that you describe.
Right now I am trying to reorganize my project and after
scratching my head for a little while (and after reading your tutorials) I came up with a setup that I am going to describe below. Since you have a lot of experience with SugarCRM, I would like to discuss it with you, just in case that I am missing
something important, which may create me problems later.
Right now I don't have a blog space yet, but I will try to get
one and I am going to describe there my setup in more details.
Basically, the structure of the svn repository is going to be
something like this:
The vendor directory contains all the sugar releases, unmodified.
The patched directory keeps all the changes that are not upgrade compatible (with all the flexibility of sugarcrm, not everything can be done in an upgrade compatible manner, as you have pointed out as well). I think to include here the
plugins as well, which may or may not be upgrade compatible (like ZuckerReports).
The directory myapp/trunk contains the application that I am building, with custom modules, customizations and everything.
The workflow is like this:
When the time comes to upgrade to a new version, it is done like this:
Of course, while developing on trunk I should be careful to make changes on the code, so that they can be applied to the database. You say that the best way for this is to not use Studio at all. However I don't like this and I will try out to find any other workaround. The best way, without doubt, would have been if Sugar developers fix Studio so that it makes changes only on the code and then applies them to the database.
Thank you for any comment or advice.
Regards,
Dashamir
#8 Sander Marechal (http://www.jejik.com)
As for not using Studio, yes it would be great if SugarCRM fixes studio to only make code changes, but don't count on it anywhere in the near future. Doing it manually is slightly more work (especially in the beginning when you're still figuring things out) but it does pay off in the end. As an added benefit you also get much cleaner code because you can pick sensible variable names. Studio and the Module Builder generate very nonsensical names, especially for relationships.
#9 dasho
7. Find the changeset of step 5 on patched/v520e and apply it on the trunk.
I am going to try out this setup and if it works well I am going to describe it on some blog page.
#10 dasho
I have already created these wiki pages where I describe my experience about using SugarCE:
http://sugarcrm.iskey.info/
#11 S. Horst
#12 Sander Marechal (http://www.jejik.com)
#13 Egor
But what do you think of approach regarding development environment given here http://www.sugarcrm.com/forums/showpost.php?p=228367&postcount=3 ? Not taking into consideration main code bug removal Is it not enough to have under CVS only custom/ directory ignoring Sugar-generated *.ext.php files and some nested directories like custom/history/, custom/metadata/ , custom/modulebuilder/, custom/working/, custom/Extension/application/Ext/ (hope I didn't miss anything important) . All the code “magic” (both manual and through Studio) happens inside custom/ directory (Am I right?). And I think in this case our custom code is pretty good master for the database if we don't forget to Quick Repair after deploying code on production. In case of building custom module in Module Builder we could build it on devel server, pack it and then deploy on production. Isn't it good enough? Can someone turn into some problems with such a workflow? I'm really interesting in your opinion.
Thanks again!
#14 Sander Marechal (http://www.jejik.com)
The deveopment aproach that was posted could work, but I am a little apprehensive about step 1 and 2. How is the code moved from localhost to the development server? Not via CVS, because that's step 3. It can work for a case where there is just one developer, but I don't see it working when there are multiple developers.
As for only keeping custom/ in CVS, that can work for module development. But you loose the ability to upgrade from one version of SugarCRM to the next using CSV. In my case, you can install a Sugar update on development, test it, commit it and then simply use a CSV update on the production server. The goal of my setup is that *every* change happens on the development server, inclusing installing modules and updates.
#15 Anonymous Coward
#16 Sander Marechal (http://www.jejik.com)
#17 Anonymous Coward
Great posts here about sugar development...
I wonder a few things...
How would you suggest to arrange customizations to the standard modules (like new fields, renaming of fields, relationships, etc...)? Would you keep them on the trunk repository, or put them on a separate repository, as you descriped for the custom modules? => my trend goes to put them under standard trunk repository...
How shall I work with language packages? => I think put them to the standard trunk repo would make sense?
The same question I wonder is for 3rd party application modules (plugins)... => also this gonna be difficult to put in a seperate repository I think, because of you never know, if they touch standard modules or not...
Thanks a lot for your response,
Rgds
#18 Sander Marechal (http://www.jejik.com)
The same goes for 3rd party modules. You can treat invasive modules just like a SugarCRM update. Install the module and commit the changes. Just remember that you need to do this for all your databases, just like core code upgrades.
#19 jak
I hope things hasn't changed much (studio vs builder) in 6.4.x now.
I am planning to use Intellij so I hope I will be able to manage these merge changes easier (esp 6.1/6.2 when I get to it)
Few clarifications.
# when you rename db to vendor-sugar-510b, will you be able to launch the sugar-510b - doesn't it hang without database?.
# Likewise, when trunk is synced by other developers how they will get the database created?
My understanding (so far) is that once installed you can neither run install.php nor launch sugar to rebuild/repair.
Is there a way to trigger Quick rebuild and repair (DB setup) without launching sugar UI?
TIA
#20 Sander Marechal (http://www.jejik.com)
Yes, it will stop working. It's supposed to stop working. After this, you should throw away your working copy (source code) and never, ever touch that database again. That database is like a subversion tag. Don't ever change it.
When you need a Sugar 5.1b instance you create a new branch, make a checkout, make a copy of the database and configure your checkout to use that copy.
They will have to configure it manually after they checked out the code. That is why the database name is not in the configuration but in .htdatabase (which you should not check into Subversion. Every developer has to create it manually after checkout).
#21 jak
Appreciate you taking time in spite of you not working on this at present.
Since we maintain the base version of different release- I thought it is just few MBs away to maintain the DBdump of each of them.
Also I would like to keep versions of the db dump everytime I install a custom package to keep track (version) at every level.
The way you have mentioned below it looks trivial but I couldn' figure out how to do it beyond changing .htdatabase to some local db name and creating the database in mysql. How do we ask the sugar to deal with this local database and import/generate the db schema.
I can post my experience on 644 post this for others looking for something similar or share their experience.
#22 Sander Marechal (http://www.jejik.com)
Use the build system to create databases, set configuration parameters and generate files like .htdatabase. You shouldn't try to setup your application from inside the application. Do it from the outside with a build system. That doesn't apply to just SugarCRM but to any kind of website.
Build systems are great at automating tasks like this. I highly recommend them. Back in 2008 when I wrote this atricle I didn't know about them yet. If I had, this article would have been a lot different!
#23 jak
Need one more help.
Since modulebuilder doesn't allow core modules(such as Accounts), what is the right way to extend them without using studio. Tried using studio, modelled extensions, export customization, installed two packages separately but it is overwriting each other. Also it is nightmarish merging them into a single package.
Appreciate some pointers.
#24 Sander Marechal (http://www.jejik.com)
Comments have been retired for this article.