Technical Blog

3 Posts tagged with the subversion tag

git-svn, meet git-p4

Many developers have started to use Git, a distributed VCS that offers a lot of advantages over centralized repositories when it comes to team development.  This is further fueled by the success of the integration tool between Git and Subversion, git-svn.  But did you know Git can also interact with Perforce?  While not as mature as its Subversion counterpart, git-p4 has the basic feature set to achieve the same objective: the ability to leverage Git for your daily work and only connect to Perforce when necessary.

The scenario

You're working on a project trunk for a customer, whose organization uses Perforce.   The customer has given you the proper Perforce access and expects your team to be committing regularly against it.  On the other hand, you need to maintain this project in your organization's Subversion as well.

 

So, you start off by doing an initial code drop to Perforce.  But what's the best way to bring your changes over?  The most obvious approach is doing a manual merge at regular intervals.  If done frequently and carefully, this may be the way to go.  However, besides the obvious disadvantages  (time consuming, human error), we also lose our original commit history.  What we would really like is a way to forward-port our commits from Subversion to Perforce.  Let's see how we can leverage Git to do this for us.

A thousand words...

svn-p4-git.png

There's a lot happening in this diagram, but the basic flow is easy to understand.

 

First, we set things up by cloning the project from Subversion and Perforce.  This is represented by Git branches master and master_perforce, respectively.

 

Meanwhile, the team hacks away and eventually, commit E is committed to Subversion.  The person performing the "merge" (we're not really merging here, it's a rebase as we will see later) ensures his/her repository is fully synced with Subversion (git svn rebase or git svn dcommit).

 

Next, what follows is the repeatable workflow to be done at regular intervals:

  1. Identify commit(s) to be ported to master_perforce.  In this case that would be commit E.
  2. Forward-port commit E from master to master_perforce, which then creates a new commit E' on master_perforce.
  3. Submit commit E' to the remote Perforce repository.

 

The rest of this article will now explain in detail the setup step as well as the repeatable workflow.

Cloning the project from Subversion and Perforce

This is one-time setup.  In fact, if you are already using git svn for your project, you can even skip the step on cloning the Subversion project.

 

Also note that the commands are based on Mac OS X with Bash.

Download and install the tools

  1. Download git at http://git-scm.com/Make sure to download 1.7.1 or later.
  2. Download git-p4 at https://github.com/ermshiperete/git-p4As Perforce recommends, do not use any other version other than the one provided by this link.
    • So we can invoke it from command line like git p4, we need to create a soft link.
  3. sudo ln -s ~/apps/git-p4 /usr/local/git/libexec/git-core
    
    1. git-p4 is a Python script, so download Python if necessary.
    2. Download p4 at http://www.perforce.com/product/components/perforce_commandline_client and add it to your path.
      sudo ln ~/apps/p4 /usr/bin/p4

          Configure Perforce client settings

          Create a properties file, say settings.config.

          P4PORT=<Perforce server URL>:<Perforce server port>
          P4USER=<username>
          P4PASSWD=<password>
          P4EDITOR=<path to text editor, e.g. "C:\Windows\notepad.exe" or vim>
          P4CLIENT=projectx_trunk_ws

          The last variable, P4CLIENT denotes the workspace you will be creating in the next step, so feel free to improvise.

           

          Create environment variable P4CONFIG and point to the properties file.

          export P4CONFIG=~/settings.config

          Create Perforce client workspace

          We need to create the Perforce client workspace.  Perforce requires a client workspace that defines a "client view".  This view determines the the path that the workspace is mapped to in Perforce.  For this article, we'll make the assumption that the path of the initial code drop to Perforce is //depot/projectx/trunk.  You'll also need to create a directory for this client workspace, I chose the same name but it doesn't have to be.

          mkdir projectx_trunk_ws
          cd projectx_trunk_ws
          p4 client projectx_trunk_ws

          The last line should launch the editor specified in P4EDITOR.  Look for the View: field which has the format [path in Perforce] maps to [path on your machine].  So, change //depot/... to //depot/projectx/trunk.

          View:
                  //depot/projectx/trunk/... //projectx_trunk_ws/...

          Save and exit.

          Test Perforce connectivity

          p4 info should give Perforce server connectivity as opposed to an error. So try it out now!

          Clone the Subversion project

          This should be famiar to those already using git-svn to connect to Subversion.

          mkdir projectx.git
          cd projectx.git
          git svn clone http://subversion/projectx/trunk .
          

          Add your standard Git configuration, etc...

          git config user.email "youremail@elasticpath.com"
          git config user.name "username"
          ...

          git branch should now show that we have master.  Not surprisingly, this represents the trunk in Subversion.

          rlim@project.git> git branch
          * master
          

          Clone the Perforce project

          cd projectx.git
          git p4 sync //depot/projectx/trunk@all

          @all will bring in all history. If you just want the head revision, leave that out.

           

          Unlike git svn clone, the command git p4 sync does not automatically create a development branch for us.  This is where we create perforce_master, which, not surprisingly, represents the trunk in Perforce.

          git branch master_perforce remotes/p4/trunk

          Where did remotes/p4/trunk come from?  Similar to git svn clone, the command git p4 sync creates a remote (tracking) branch named remotes/p4/trunk to track changes made in the Perforce trunk.  You don't do work on this branch, you need to check out this branch to a development (topic) branch, master_perforce.  Although not shown, git svn clone also created a remote branch behind the scenes.  The difference is it automatically created a development branch master off this remote branch for us.

          Where are we now?

          That's a whole lot of set up, but thankfully it is just one-time.  git branch should now show two branches.

           ______________________
          rlim@projectx.git> git branch
            master
          * master_perforce

          Identify the commits

          This signals the start of the repeatable workflow as described previously.  In this step we will need to identify the "candidate" commits - those that we want to port over to master_perforce.  This boils down to determining the start and end point for porting.  Any commits between these "points" will be ported over.

           

          Remember, you've done initial code to Perforce, which means at some point in Subversion history, the code was copied to Perforce.  Since we've cloned the history from Subversion, we have the commit history available in Git.  This needs to be figured out in relation to the most current commit, HEAD.  For our example, let's assume this was done at HEAD^.  Couple of ways to look at it just to drill the point home:

          • It was one revision ago (HEAD^) that we dropped the code to Perforce, or ...
          • ... since we dropped the code to Perforce, we've made one commit in Subversion - that's commit E.

          In Git, a symbolic reference is a name that points to an object.  HEAD is such a symbolic reference and the object that it points to is the most recent commit of the branch you are currently in.  Because a parent commit is always reachable from any  commit, we can refer to previous commits using the notation HEAD^ (previous commit from most recent) or HEAD~1 (previous commit again) or HEAD~2 (two commits back from most recent).  Much easier than specifying the commit object's SHA-1 hash!

           

          So, we now know when we did the initial code drop, and it also means commit after this we'll need to port over to master_perforce.  The initial code drop becomes the starting point for porting.  Let's tag this initial starting point.

          git checkout master
          git tag initial_code_drop HEAD~1
          

          What about the ending point?  Assuming we want all commits ported over, the end point is already defined for us - that's HEADPutting it together, we want all the commits between HEAD~1 (excluding) and HEAD (including).  In our simple, hypothetical scenario, that's just one commit, E

          All Your Rebase Belong to ... Git?!

          We have identified the starting and end point in which to port our commits over from master to master_perforce.  Here's the actual commands to do the actual forward-porting.  The lines are numbered for further analysis.

          1   git checkout master
          2   git branch -f start initial_code_drop
          3   git branch -f end HEAD
          4   git rebase --onto master_perforce start end
          5   git branch -f master_perforce end
          

          1 - Checkout master branch.

          2 - Rebase works off branches.  So, we must create branch start (denoting our starting point) off the tag initial_code_drop.

          3 - Same reason as previous, except for branch end (denoting our ending point) off HEAD.

          4 - This command reads, "forward port all commits onto master_perforce after start (remember, it's excluding) and ending at end".

          5 - After the previous command is executed, master_perforce has not changed but end has been rebased onto it.  We need to re-point master_perforce to end (also known as a fast-forward merge of branches master_perforce and end).  For those uninitiated to rebasing and branching with Git, search for "rebase" on the Wiki and hopefully my rambling there will shed more light on this.

           

          Line 4 is the one that does the heavy lifting.  Git takes each commit and applies it (as a patch) sequentially to master_perforce.  We also have two branches, start and end that are no longer needed.  It's safe to delete these branches (e.g. git branch -d start), but you don't really have to, since they will be used again the next time around.

          Tag new starting point for next round

          As the title states, we should tag where we last left off.

          git tag 09Dec HEAD
          

          This will become the new starting point (instead of initial_code_drop) for the next round of rebasing!  By using the current date, I know when was the last time I performed this workflow (to see the actual commit that was tagged, i.e. HEAD at that time, git show 09Dec).

           

          If you look at the 5 lines above again, it means everytime we do this, the only line that changes is line 2!  Better yet, dump these lines to a script that take a single argument - the starting point.

          Committing to Perforce

          Hopefully this should be the easiest step of the workflow.  Review your changes, as this is the point of no return!  If you want to revert before the rebase, the commands git checkout master_perforce and then git reset --hard HEAD~1 will undo commit E'.

          git checkout master_perforce
          git-p4 submit
          
          

          Each commit will bring up the editor specified in P4EDITOR.  Notice the commit message is already populated with the exact message that was committed in Subversion for commit E.

          Final thoughts

          Porting a commit that was previously ported

          If you lose track of the starting point, and accidentally port over a commit that was previously ported, don't fret!  The rebase operation works on patches and Git is smart enough to know it was already applied previously.  For this commit you will get a message like No changes – Patch already applied.  Git will happily continue along applying the other commits sequentially.

          Cherry picking a range of commits

          As of Git 1.7.2, there is another approach to port commits over instead of using rebase.  You can specify a range of commits with git cherry-pick start..end, which would essentially be a one liner operation instead of five lines above.  However, I have yet to try this and secondly, I don't believe cherry picking offers the chance to continue after a merge conflict as rebase does.

          0 Comments Permalink
          Background

           

          Our client's e-commerce store has been powered by Elastic Path for well over a year now. Our team started development of the store on a beta version of Elastic Path 6.0 in January 2008. After 3 months of development, including a merge with the public release of Elastic Path 6.0, we launched the store in March 2008. We continued to add new features and bug fixes. We even took advantage of EP 6.0's multi-store support to add a few more stores in August 2008. At the beginning of 2009, after a year's worth of customizations atop the client's EP 6.0 deployment, we decided that it was time to upgrade to EP 6.1.
          timeline.jpg



          Why Upgrade?

           

          Upgrading a customized version of Elastic Path Commerce is not a trivial process. So you need to ask yourself (and the client) if an upgrade is even necessary. The first step of upgrading Elastic Path is determining whether the benefits of upgrading exceed the costs of upgrading. The cost is time and money.

           

          What are the benefits? Here are some of ours:
          • EP 6.1 provides full multistore. In EP 6.0, multiple stores required multiple storefront WAR files: one for each store. EP 6.1 runs multiple stores from a single WAR file. Less WAR files translates to quicker deployments and a smaller resource footprint.  This is especially relevant to us since our client is running six stores.
          • EP 6.1 comes with 300+ bug fixes.
          • Upgrading to EP 6.1 paves the way for upgrades to future versions of Elastic Path. This is crucial, because our client is anticipating features that are planned in future versions of EP (e.g. import/export of promotion data, available in EP 6.1.1).
          • EP 6.1 comes with FIT integration tests.
          • EP 6.1 introduces the import/export utility, used to migrate catalog data between databases.
          • EP 6.1 introduces the settings framework, an improved way to manage configuration settings.
          • EP 6.1 allows search index rebuilding to be triggered from the CM Client.
          • The Store configuration UI has been overhauled in EP 6.1; the Store wizard has been replaced by a Store editor.
          • In EP 6.1, assets (e.g. velocity templates, images, etc.) are stored outside the web application, making the view layer much easier to modify.

           

          We presented a list of EP 6.1 benefits to our client, provided an estimate on the amount of time it would take to complete the upgrade, and got the green light to go ahead with it. These were the benefits relevant to us, but EP 6.1 has many other improvements. For example, here are 7 Big Code Changes in EP 6.1.



          The Upgrade

           

          After we convinced ourselves and our client that upgrading to EP 6.1 was worthwhile, we moved on to the fun part: the upgrade itself. There are several steps that need to be completed during an upgrade and it's easy to become overwhelmed at first, so we broke the upgrade down into more manageable sub-tasks:
          1. Database updates
          2. Merging the code
          3. Moving customized named database queries
          4. Using the new settings framework
          5. Relocating the assets directory
          6. Setting up Maven
          Let's cover each sub-task in more detail.

           

          1. Database Updates
          The database schema did not change significantly between EP 6.0 and EP 6.1. Elastic Path has a script that upgrades the database from EP 6.0.x to EP 6.0.3 and another that upgrades it from EP 6.0.3 to EP 6.1. We ran both of these scripts against our database with no problems. Most of the schema changes consists of new tables and new columns and there are few changes to existing columns. Click here for detailed information about the EP 6.1 database updates.

           

          After upgrading the schema with the scripts, a few data updates needed to be made:
          • By default, full credit card numbers are stored in the database for each order; we turned this off (the ability to turn it off is a new EP 6.1 feature). This is accomplished by setting STORE_FULL_CREDIT_CARDS to 0 for each row in TSTORE.
          • Populate the new TSTORESUPPORTEDLOCALE table with store-locale mappings; these mappings mirrored the catalog-locale mappings already in TCATALOGSUPPORTEDLOCALE.
          • Populate the new TSTORESUPPORTEDCURRENCY table with the store-currency mappings; these mappings are similar to the catalog-currency mappings already in TCATALOGSUPPORTEDCURRENCY.

           

          2. Merging the Code
          The majority of the time spent on the upgrade was in this step: merging code. Generally, the more changes you make to a codebase, the harder it is to upgrade in the future. I say "generally" because there are ways to customize code that minimize future code merge conflicts. For example, this GREP technical blog post shows how to customize code with the decorator design pattern; a strategy that significantly reduces code conflicts.

           

          The easiest way to merge large amounts of code is by using your version control software's merge tools combined with vendor branches. We have a great GREP article explaining what vendor branches are and how they can be used for merging code here. Since we use subversion for this particular project, we ended up using svn merge to merge the code.

           

          This is the svn merge command we used:
          svn merge https://svn.elasticpath.com/perfect_code/pd/ep5/tags/publicrelease6.0/ https://svn.elasticpath.com/perfect_code/pd/ep5/tags/publicrelease6.1/ . --accept postpone
          This command takes the differences between OOTB EP 6.0 and OOTB EP 6.1 (whose vendor branches are specified in green and blue respectively), and applies those differences onto our customized codebase (specified in red, this command was run from our project's root level directory). The --accept postpone part tells the utility to postpone resolving any merge conflicts that occur.

           

          The following diagram shows the branches referred to in the svn merge command and our desired "Customized EP 6.1" end state:
          codemerge.jpg
          When svn merge hits a conflicted file, it produces 3 output files. For example, consider a file called MyClass.java that is conflicted; svn merge will produce the files MyClass.java.left, MyClass.java.right, and MyClass.java.working. The highlighted colors correspond to the branches in the svn merge command above. The .left file is the EP6.0 version, the .right file is the EP 6.1 version, and the .working file is our customized codebase version.

           

          The utility does a pretty good job at merging the source files. Some stats we gathered after running this command:
          • 3682 files were added
          • 118 files were deleted
          • 953 files were automatically merged successfully
          • 132 files were conflicted and not automatically merged
          The high number of files added is largely attributed to the relocated assets directory and the new FIT tests. The 953 files merged successfully demonstrates the advantage of using a utility to automatically merge files. Unfortunately, not everything can be automatically merged; we had to manually merge the 132 conflicted files. These numbers will vary depending on the type and amount of customizations you've made on your codebase; our codebase has a lot of customizations.

           

          There are many approaches to manually merging a conflicted file. The .left, .right, and .working files produced by svn merge, described above, are invaluable to this process.

           

          This is my strategy:
          1. diff (i.e. compare) the .left file (EP 6.0 version) with the .right file (EP 6.1 version); this shows Elastic Path's changes to the file from version 6.0 to 6.1.
          2. diff the .left file (EP 6.0 version) with the .working file (your customized version); this shows your customizations to the file from 6.0 to its current state.
          3. Based on the comparisons above, who changed the file more? Elastic Path or you?
            • If Elastic Path made more changes to the file, start with the .right file and incorporate your changes (indicated by the diff in step 2) to the file.
            • If you made more changes to the file, start with the .working file and incorporate Elastic Path's changes (indicated by the diff in step 1) to the file.
            • If they have the same amount of changes, it doesn't really matter which version you pick as your starting point; the amount of work will roughly be the same. I lean towards picking the .right file (EP 6.1 version) because aligning your codebase to Elastic Path's will make future upgrades smoother.
          4. After merging the file in step 3, you are done. Move onto the next file!
          More numbers. Of the 132 conflicted files:
          • 88 were relatively easy/trivial merges
          • 23 were moderately complicated merges
          • And 21 were very complicated merges
          The very complicated merges were the files that were changed significantly by both us (i.e. our customizations) and by Elastic Path (during the development of EP 6.1). These files include the class files for ShoppingCart, OrderService, CheckoutService, and other areas of Elastic Path that are often modified.

           

          All in all, the code merge is likely not as painful as most anticipate it to be. Even with 40+ files requiring non-trivial manual merges, our code merge went pretty smoothly.

           

          A couple tips:
          • Take advantage of dry-run modes of your merging tool to preview how much work needs to be done without actually performing the merge. For example, adding the --dry-run option to our svn merge command enabled us to see how many merge conflicts we'd have to resolve in advance of the merge itself; this information is very useful during the estimation phase.
          svn merge https://svn.example.com/project/tags/publicrelease6.0/ https://svn.example.com/project/tags/publicrelease6.1/ . --accept postpone --dry-run
          • As we manually merged the conflicted files, we took note of the merge details in a list. For each file that required non-trivial merging, we jotted down a couple sentences describing the merge (e.g. which methods were merged, etc.). Taking notes during the code merge requires a little overhead, but the list proved to be very useful when bugs caused by the merge were found later on.

           

          3. Moving Customized Named Database Queries

           

          In EP 6.0, named JPA database queries were embedded in the class files. For example, this bit of code is in CatalogImpl.java:
          @NamedQueries({
              @NamedQuery(name = "CATALOG_IN_USE_BY_PRODUCT_TYPE",
                      query = "SELECT pt.uidPk FROM ProductTypeImpl pt"
                              + " LEFT JOIN pt.catalog c WHERE c.uidPk = ?1")
              ...
          })

          In EP 6.1, the named JPA database queries have been moved from the class files to separate object-relational mapping XML files. The query above has been moved to the catalog-orm.xml file.
          The code merge should take care of the OOTB (out-of-the-box) named queries but any customized name queries that we added had to be manually moved from the class file to the appropriate XML file. 


          4. Using the New Settings Framework

           

          In EP 6.0, configuration settings were stored in various XML files (e.g. commerce-config.xml, default.xml, etc.). EP 6.1 introduces a new settings framework that stores settings in the database and allows them to be managed from the CM Client; the setting values are retrieved wherever they are needed in the code via the SettingsService class. The new settings framework is a big improvement for managing configuration settings, but some work needs to be done to take advantage of it.

           

          First, some of the OOTB setting values need to be overridden with your project's values. For instance, our client's stores use Elastic Path's one-page checkout. There is a boolean setting that turns this feature on. By default, this setting is off. In EP 6.0, we turned this setting on by modifying a line in commerce-config.xml: onepage.enable=true. In EP 6.1, this setting value is no longer retrieved from commerce-config.xml (although the value may still be there depending on how it was handled during the code merge), but it is not being used. The setting value is now retrieved from the database via the new settings framework. We located the setting (COMMERCE/STORE/enableOnePageCheckout) and changed its value from false to true with a database update. After we were done modifying the OOTB setting values, we removed all unused setting definitions from the XML files.

           

          Second, we had to migrate all of the custom settings we created to the new settings framework. We have approximately 60 custom settings -- much more than typical projects. Migrating custom settings is not absolutely required since they are still parsed from XML files and retrievable using the ElasticPath class, but there are many advantages of using the settings framework.

          To migrate a custom setting, you first need to create the setting definition with a database insert statement. For instance, the following insert statement creates our "tradeshow timeout" setting:
          INSERT INTO TSETTINGDEFINITION(UIDPK, PATH, DEFAULT_VALUE, VALUE_TYPE, DESCRIPTION, MAX_OVERRIDE_VALUES)
           VALUES(81, "CUSTOM/tradeshowTimeout", "120000", "Integer", "Indicates the tradeshow timeout interval, in seconds.", -1);
          
          Next, find all the places in the code that use this setting. Modify the code to use the SettingsService to retrieve the setting value from the database instead of from the XML files (via ElasticPath). Going back to the example above, we make the following javascript change:
          timeoutId = window.setTimeout("resetTradeshowPage()", $ctxStoreConfig.getSetting("CUSTOM/STORE/tradeshowTimeout").getValue());
          
          ...and the following java code change:
          final String settingValue = getSettingsService().getSettingValue("CUSTOM/STORE/tradeshowTimeout").getValue();
           
          
          During our merge, we went one extra step and implemented environment-specific settings, a feature that is not available out of the box and that required some additional customization. You can read more about environment-specific settings in this technical blog post on GREP.

           

          5. Relocating the Assets Directory

           

          In EP 6.1, the assets directory was moved outside of the war file. For example, storefront templates were previously located in com.elasticpath.sf/WEB-INF/templates/; they are now located in assets/themes/storecode/default/templates. Note that the storefront templates were not the only ones that were relocated; CM templates, like those used for order confirmation emails, were also moved from the com.elasticpath.sf project to the new assets directory. The relocation decouples the view (i.e. templates, images, etc.) from the model and controller (the application). There are many advantages to this, including the ability to easily hot-swap template changes. However, the relocated assets directory made the code merge more complex.

          Depending on your merging tool, the automated code merge may handle the relocated assets directory differently. We used svn merge. Unfortunately, svn merge was not smart enough to realize that the assets were relocated; it assumed that the assets directory was simply removed from its old location and that a new assets directory was added to its new location. As a result, the files were not merged at all. The new assets directory was the OOTB version.

           

          Thus, we needed to manually merge all of our template changes. This process is not as bad as it sounds, since Elastic Path did not make any major changes to the templates from EP 6.0 to EP 6.1. We merged most of the templates by first diffing the EP 6.0 and EP 6.1 versions and then adding those differences to our customized versions.
          Some of the bigger template changes made between EP 6.0 and EP 6.1 include:
          • Retrieving the store and catalog not from the ElasticPath object anymore but from the new StoreConfig object
          • Using the #emailMessage macro (instead of the #springMessage macro) in the email templates to display localized messages
          Also worth mentioning is the new layout of the assets directory. Previously, all templates where put into a single templates directory. In EP 6.1, there is support for multiple themes and store-specific templates. For instance, the view-cart.vm template file may live in /assets/themes/mytheme/store1/templates/velocity/shoppingCart; this means that the store whose storecode is "store1" and whose theme is "mytheme" will use that particular view-cart.vm template. Another store, whose theme setting is set to "anothertheme"and doesn't use store-specific templates would have its view-cart.vm template in /assets/themes/anothertheme/default/templates/velocity/shoppingCart. During the merge, we made sure to set up the assets directory structure in a way that would best work for us.

          Note: I mentioned above that template files can now be easily hot-swapped. One thing to keep in mind is that by default, templates are cached when the application starts up. If you hot-swap or modify a template file, you need to force a cache refresh by invalidating the cache. A quick and easy way to do this is to open the
          invalidate-cache.ep URL in a web browser.

           


          6. Setting up Maven

           

          EP 6.1 uses Maven to manage project dependencies. In the past, if a project required a component (e.g. a 3rd party library) that was not used OOTB, we would put that component in the libs directory. With Maven, there's no longer a need for a libs directory, so we were able to remove it altogether during our merge. There was one caveat however: we were using a 3rd party library for GeoIP lookups (Maxmind). We added the dependency to our GeoIP library by adding the following in the storefront's pom.xml file:
          <dependency>
              <groupId>maxmind</groupId>
              <artifactId>geoip</artifactId>
              <version>1.2.1</version>
              <scope>compile</scope>
          </dependency>
          
          The storefront is now dependant on the Maxmind GeoIP component. However, Maxmind is not available OOTB so we also had to make sure that Maven would be able to download the library files from somewhere.
          Elastic Path uses Archiva repositories to store the dependent libraries. Rather than adding the Maxmind files to the internal Elastic Path repositories, we decided to add it to our team's Archiva repository. Archiva has a nice web interface to upload jar files into the repository. We added the following to elasticpath.pom to tell Maven that it needed to look in our team's archiva repository (maven-ps.elasticpath.net) for some of its dependencies:
          <repository>
              <id>ps-extras</id>
              <name>Elastic Path PS Extras Repository</name>
              <url>http://maven-ps.elasticpath.net:8080/archiva/repository/extras/</url>
              <releases>
                  <enabled>true</enabled>
              </releases>
              <snapshots>
                  <enabled>false</enabled>
              </snapshots>
          </repository>
          

          To sum up, setting up Maven should require no work if your project does not have any custom dependencies. If your project does have dependencies that are not included with OOTB Elastic Path, they will need to be defined in the project object model (POM) files.

           


          Testing & Bug Fixing


          Our QA team's first task was to verify that EP 6.1 worked on our test servers. We grabbed the OOTB public release version of EP 6.1, deployed it on our test machines, and ran through a smoke test. Once we were able to confirm that the OOTB EP 6.1 worked on our servers, we focused on our merged codebase.

           

          The code merge modified numerous files across the entire project. As a result, a full regression test on all of our customizations was required to verify that the code merge was done correctly. It goes without saying that the full regression test took a considerable amount of time to complete. The faster the code is merged, compiled cleanly, and deployed to test servers, the sooner testing can begin. After merging the code, we pushed a testable build out as soon as we could so that our QA team could begin testing. The first half-dozen builds contained many blocking bugs, but they were still testable. The key is to fix the blocking bugs quickly so that the QA team can be kept busy with the full regression test.

           

          The JUnit tests should also be run and fixed as soon as possible, preferably even before the first testable build is deployed; they are a great way to catch bugs at the source code level.

           

          The new FIT tests in EP 6.1, however, were a little more difficult to run. They require some setup and many may be broken after the code merge depending on the customizations. We skipped the FIT tests during the code merge and instead chose to run them after the upgrade.

           


          Time Estimates

           

          There's no definitive answer for how long an upgrade will take. It depends on the amount and type of customizations. For our client's upgrade, the entire process -- including scoping, upgrading, testing, and bug fixing -- took three months. One developer completed the upgrade (i.e. database updates, code merge, etc.), two QA analysts performed the testing, and three developers tackled the bugs found by the testers. 8% of our budget was spend on scoping and estimating, 60% was spend on the actual upgrade tasks and bug fixing, and 32% was spent on testing.

           

          These numbers will vary from project to project. For example, the client's custom settings required additional custom work, which took approximately two weeks to design and implement. This task is something we developed specifically for this project only.

           


          Conclusion

           

          Upgrading Elastic Path is neither trivial or impossible. Elastic Path provides documentation on the various aspects of upgrading to Elastic Path 6.1 (database updates, settings migration, assets relocation, etc.), but the complete story is different for each project. The amount of work depends on the number and type of customizations that have been made. If you have any questions or comments, feel free to post them here.

           

          0 Comments Permalink

          Alternative title: How to Make Your Life Easier and Not Go Bald from Pulling Your Hair out Trying to Keep Current on a Constantly Evolving and Shifting Codebase

           

          This is probably the biggest and most avoided question from all of our successfully launched Professional Services projects. How can we upgrade to the greatest and latest on our heavily customized EP project? Let's be clear that performing a point release upgrade, or even consuming the latest sprint of code from our extremely productive Product Development group isn't trivial. A full version upgrade is even more daunting and usually heavily balanced against the cost of re-implementing from scratch.

           

          First off, hopefully you are following our advice to try to avoid customizing EP code as far as possible, as noted in our documentation and tutorials. Following this methodology will make your life a lot easier, as upgrading core EP classes that have only minor changes will be a lot easier.

           

          With that in mind, the best method through tried and tested experience, from brute force patching and merging, to re-implementing large customizations, is to take advantage of the merge tools from your chosen source control software (you are using source control I hope! If not, this book will be your best friend). Why do all the manual merge work when X does it so well already (X being subversion, perforce, cvs, vss, git, the next big flavor of the month). The key to leveraging your source control software to facilitate an upgrade is the concept of a vendor branch.

           

          Working with a vendor branch is straight forward. This is applicable to any 3rd-party library you are using and modifying, not just EP. Take a quick gander at these various interweb sites for a better explanation than i can reasonably provide. For our purposes, a vendor branch for EP is an untainted, pristine copy of EP source. Don't just take our beloved zip of source code and stash it in your network drive! Check it in and show it off! You can branch your customized codeline off the vendor branch and start working away with the enthusiasm of a frenzied squirrel in the spring. The next time a new version of EP comes along, simply drop it directly on top of the current vendor branch of EP in your repository. With this new EP revision, you can perform your normal merge routine of just the diff between these EP revisions and merge the delta of changes into your customized eCommerce solution. And, if you have been following the procedures to avoid modifying EP source, conflicts should be kept to a minimum, files will just resolve themselves, and you can go out for a quick pint. Sure sounds easy huh? But how about in real life?

           

          Well, let's see how we've taken this and made our lives easier at a client site in Perforce. I'm not the biggest Perforce fan, but I sure appreciate it's advanced branching and visual merging capabilities. We've set this up as follows:

           

          • EP_IMPORT, our EP vendor branch containing 100% pure EP 6.1 eCommerce goodness
          • Stable - our main development branch, full of customizations of the greatest and latest variety

           

          Now, presume we're taking in change after massive change from our Product Development group. They've given us EP 6.1.1! Hooray! But how do we get this into our source control? Looks easy enough. Drop it on top of EP_IMPORT and check it in? Whoa, hold on there, code cowboy. This baby probably won't compile just with that.

           

          The main things to remember is to collect three things with each new drop of code:

           

          1. All files that have been updated in 6.1.1
          2. All files that have been deleted in 6.1.1
          3. All files that have been added in 6.1.1

           

          Once you have all three, you can mark the relevant files for delete, add and update. I've taken a short cut to calculate the files that fall into the above three buckets in this case. I have access to our internal subversion repository at EP, so I'm just going to take a log of files taken in by 'svn update' when updating from the 6.1 tag and the new 6.1.1 tag. You can essentially calculate the same bucket of files using the diff command. But in my case I'm just taking a shortcut and doing:

           

          svn update > merge.txt

           

          This logs out for us a lot of lines of this variety:

          U    com.elasticpath.core/WEB-INF/src/main/java/com/elasticpath/service/rules/PromotionRuleDelegate.java
          D    com.elasticpath.core/WEB-INF/src/main/java/com/elasticpath/service/misc/impl/WorkAroundOpenJPAFetchPlanHelperImpl.java
          A    com.elasticpath.core/WEB-INF/src/main/java/com/elasticpath/service/misc/impl/OpenJPAEventListeningFetchPlanHelperImpl.java

           

          With some help from sed (for the non-Unixers, feel free to grab and install cygwin, or your favorite port of the beloved Unix command line tools), I'm going to parse the log and split it into three files:

          cat merge.txt | sed \-e '/D /d' | sed \-e '/U /d' | sed \-e 's/A[ ]*//' > adds.txt
          cat merge.txt | sed \-e '/A /d' | sed \-e '/U /d' | sed \-e 's/D[ ]*//' > deletes.txt
          cat merge.txt | sed \-e '/A /d' | sed \-e '/D /d' | sed \-e 's/[ ]*[U]*[ ]*//' > updates.txt

           

          Now, Perforce is a bit finicky in that I need to mark which files I'm going to edit to remove the read-only permissions of relevant files. Sure, I can mark every file for editing, but let's play it safe:

          pwd
          /perforce/depot/branches/EP_IMPORT/
          cat updates.txt | xargs p4 edit -f

           

          Next, time to mark the deleted files for removal:

          cat deletes.txt | xargs p4 delete -f

           

          Finally, it's time to unzip on top all the files from the new EP 6.1.1 source zip and mark our new files for add:

          tar -xvzf EP6.1.1_Export.tgz .
          cat adds.txt | xargs p4 add -f

           

          That does it! Now check that all in! Now we have EP 6.1.1 in our EP_IMPORT branch. Time to use the impressive P4V tool from Perforce to integrate from the EP_IMPORT branch into the Stable branch. Since we've avoided modify EP source as much as possible, the majority of the changes should auto-resolve. Clean up some of the conflicts and tweak the customizations as necessary, run the normal routine of compile, checkstyle, pmd, unit tests, integration tests and your project should now be ready to go. Okay, that's a gross over-simplication of the conflict resolution and regression testing process that goes hand in hand with a version upgrade, but hopefully you see how a vendor branch does wonders in facilitating an easier upgrade process with custom code. No more awkward patch files and brute force.

           

          Assuming you don't have access to our subversion to get a hold of the list of changes, you can use diff to log file differences and files missing from the 6.1 and 6.1.1 codebases.

           

          diff -r /perforce/depot/branches/EP_IMPORT /tmp/EP611/ > diffs.txt

           

          This should give you a nice log that is easily parsed into the same add/delete/update buckets. So now you're freshly armed to do the upgrade that has been haunting you late at night. Vendor branches and merge tools are your new best friends! Happy upgrading and merging.

           

          Drew

          0 Comments Permalink