Salesforce Source Control and Release Process

This post outlines my preferred approach to managing parallel developments on the Salesforce platform in what I refer to as the Converged Programme Model. I readily acknowledge that there’s a multitude of ways to accomplish this each with it’s own subjective merits. Before adopting a parallel work-stream model take the time to understand the technical complexity, process overhead and time investment required. Of particular concern should be the team’s readiness for such a disruptive change. In my experience it’s better to plug any skills gaps upfront, be very prescriptive with process guidance, start-small and build out incrementally – the risk otherwise is considerable. Typically resistance will come from individuals unaccustomed to a disciplined approach to software development/release process.

SCC

Objectives

  1. Concurrent Development. Support parallel programme workstreams converging into a shared production Salesforce environment.
  2. Automation. Deliver build automation – reducing the manual overhead required to deploy between environments.
  3. Gold Standard. Deliver a best practice approach – the initial design should scale up and down in response to changing programme conditions.
  4. Non-disruptive. Facilitate a staggered approach to adoption – enabling key benefits to be realised quickly without disrupting productivity.
  5. Minimise Release Overhead. Project branches should be regularly and incrementally updated from the master branch – reducing the inherent risk of divergence over time.

Tools

  1. GitHub
  2. - Get started with public repositories, upgrade to a paid plan and use private repositories for any source code you don’t want to share with the world at large.

    - Create an Organisation account to enable Team functionality.

    - Key benefit versus Subversion (CVS etc.) is fast and efficient branch management; parallel workstreams are managed on branches with frequent merging.

    - It is possible, albeit time expensive to implement a Git server within the enterprise. In my view the GitHub administration interface alone is worth the price.

  3. Jenkins
  4. - Deployed on a Windows EC2 instance with an elastic IP. A free usage tier, micro instance provides an ideal server host. Using a Linux host can be beneficial in regard to SSH authentication from Jenkins to GitHub. This is just one advantage of many. Pick the Operating System/Platform the team you’re working with are most familiar, a Linux host that only one team member can administrate makes no sense.

    - On Windows the Jenkins service should be configured to run a specific user account (with least privileges assigned). This is required to generate the key files for SSH authentication.

    - Enable Jenkins security. Particularly relevant if the host is open to the public web. Lock the inbound IP ranges via the EC2 security group if possible.

    - Either store the Ant build files (build.xml, build.properties) in the Git repository or use an XCOPY post-build step to copy the files into the workspace from a file system location – as below. I prefer to keep the build files external to Git – there shouldn’t be any need to version manage such files – plus the build.properties file may contain passwords in plaintext.

    Jenkins Job Build Config

    - Install GitHub and Git Plugins
    Required to build from a GitHub repository and enables build automation via Post-Receive Hooks. Under Jenkins System Configuration; configure “Manually manage hook URLs”, this requires your GitHub repository to have the hook set manually via Service Hooks under repository settings. Add a [Jenkins (GitHub plugin)] service hook like http://yourservername:8080/github-webhook/. The message sent on git-push to the remote repository will trigger any Jenkins job that builds from the branch that has been updated and has the [Build when a change is pushed to GitHub] option set to true.

    - SSH Keys
    In order to use SSH from Jenkins to a private GitHub repository, SSH authentication is required, which uses a generated key pair. The public key is added as a Deploy Key in GitHub under repository settings. This works well but if you want the same Jenkins user to access multiple repositories over SSH you have a problem as each Deploy Key must be globally unique across all GitHub repositories. The answer to this is to use aliasing and a SSH config file (refer: http://www.onemogin.com/blog/2011/9/1/jenkins-and-github-multiple-private-projects.html) however this won’t work with Post-Receive Hooks as the repository URL in the sent message won’t match to the aliased repository URL in the Jenkins job – typically errant behaviour below from the Jenkins log. I can’t see a way around this at the time of writing this post.

    FINE: Skipped GitHub Test - buildautomationtest repository because it doesn't have a matching repository.
    May 7, 2013 6:21:35 PM com.cloudbees.jenkins.GitHubWebHook
    FINE: Considering to poke GitHub Test - buildautomationtest repository
    

    - Chatter Plugin
    I’m a big fan of this plugin by Simon Fell. I tend to use a dedicated release manager user, e.g. release.manager@force365.com, standard user license capacity permitting, and perform all deployment tasks in this user context. This approach provides clarity on changes made by a deployment versus actual user and provides an easy way to be notified of failures etc.

    Key Principles

    1. Fit-for-purpose Org-set
    2. - Org-set is the terminology I use to describe the collection of orgs, and their roles, required to deliver a project safely to production.

      - One size does not fit all. Pick the minimum set of orgs roles required to deliver the project. Each org is a time expensive overhead.

      - Sandbox types. In defining the org-set, factor in the availability of config-only and full-copy sandboxes. The latter must be retained for cases where infrequent refresh is required. Project-level orgs don’t need to be part of the sandbox estate, Developer Edition orgs, or perhaps Partner Developer Edition orgs can be employed. Full-copy sandboxes are incredibly expensive, valuable resources, use only when absolutely necessary for as wide a set of roles as possible.

      - Connected orgs. For projects involving complex integrations, the complexity involved in creating a connected-org may influence the org-set design – there may be an argument to consolidate roles onto a single test org used for QA and UAT perhaps.

    3. Continuous Integration
    4. A best practice org-set design for non-trivial technical projects with multiple technical contributors should require isolation of developer activities into a separate developer orgs with a code-level integration org and Continuous Integration (CI) process in place.

    5. Project-level sandboxes are not refreshed
    6. Project-level orgs are all built from the Git repository. The Pre-production programme-level org must be refreshed from Production pre-deployment to ensure the deployment is verified against the current state.

    7. Commit to the remote project branch is a commitment that metadata is ready for system testing
    8. Build automation will deploy a project branch commit to the project QA org. In my experience it pays to be prescriptive in terms of development process.

    9. Commit to the remote master branch is a commitment that metadata is ready for integration testing
    10. Build automation will deploy a master branch commit to the programme INT (integration) org – this org exists to enable rigorous regression testing to be applied by all project workstream. Post-deployment suites of automated tests should be invoked and reports analysed by the test lead on each project.

    11. Test Automation
    12. It’s a significant resource overhead to execute manual test scripts for each regression test cycle, not to mention error prone. For non-trivial projects, the investment must be made at an early stage in automated-testing. Selenium is a good choice, but the tool utilised doesn’t really matter, what matters is that from the outset of the project the test team start to build-up a comprehensive suite of automated test cases with coverage of the key acceptance criteria defined for each user story. The suites then enable automation of regression testing during deployment phases – the same scripts underpin system testing and provide an often overlooked second stage to CI (unit tests + acceptance tests).

    13. GitHub branch design
    14. - A simple, clean branch design is desirable in the remote repository.

      - Long-lived branches for active project workstreams. Project branches may have sub-branches for each sprint or phase.

      - Long-lived branch for patches. Bug fixes are developed on local branches and committed to the remote support branch when ready for system testing.

      - It can be advisable to consider how important a clean Network Graph is, this is impacted by Git merge versus rebase decisions.

    15. Build automation challenges
    16. In a perfect world, all metadata component types would be covered by the Metadata API. This isn’t the case so the nirvana of simple cloning of an org configuration is yet to exist. Instead a prescriptive process is required which spans manual configuration tasks, metadata deletion and build automation.

      - Proactive management of change
      A nominated release manager should proactively manage change at a programme-level, advise the project teams on release process and strive to minimise deployment conflicts through early involvement in all project developments. A change log should be maintained which lists all changes being made. This could include technical component types (ApexClass, ApexTrigger etc.) being added, modified or deleted, but as a minimum must track configuration changes requiring manual action – enablement of features, field data type changes etc. and required standing data (custom settings etc.). All changes should be mapped to a Change Type of manual or automated and a list of orgs to which the change has been deployed tracked. This is clearly an overhead to the project but without control it can be very easy to lose track of the current state of the orgs in use and face significant time expense in attempting to rationalise the situation through failing deployments. The release manager, or technical lead should apply manual tasks to target orgs pre-emptively to minimise automated build failures.

      - Be prepared for build failures
      Automated builds will fail; this is a fact of life where build-dependencies on manual actions exist. Proactive management will only get you so far. Attempting to minimise this is more realistic than elimination.

      - Data
      Automation of data setup in a target org is possible via Ant and the Data Loader CLI, or other similar means. Alternatively a data file could be deployed as a document or static resource and then loaded from an Apex script (as per the ISV approach).

      - Unsupported metadata component types
      Automation is possible using Selenium scripts, which execute at the UI level and can simulate, for example, a user activating a setting. Such scripts can then be integrated into an automated build. This is highly possible, but takes time and expertise with both Ant and Selenium to accomplish.

    17. Programme-level Integration
    18. The Converged Programme Model involves project workstreams building in isolated org-sets with frequent merge-from-master actions bringing across any changes to the production state. This approach should surface conflicts early, i.e. during development itself, but to be sure that shared component changes have not introduced any functional inconsistencies, regression testing must be applied by each and every project workstream on each occasion any project does a release. This is a strong argument for test automation.

    19. UAT
    20. - Project-level or programme-level?
      In principle UAT should always be applied at the local project-level as the commit to the programme-level integration org is an absolute commitment that the code is production ready. In practice UAT may be two tiered; initial user acceptance of new functionality, followed by some form of secondary acceptance testing in Pre-production, in parallel to deployment verification testing.

    21. Path-to-production Change Management
    22. As with any programme of work, fit-for-purpose Change Management processes should be in place. In context this means a Change Advisory Board (CAB) should be in place to approve deployment, this must include informed and empowered representation across business and technical functions.

      - A Deployment Request Form (DRF), or similar, should be produced to document the change being released, the impact, pre and post deployment tasks, GitHub commit # etc., approval date or rejection reason. The DRF could be approved by a convened board or via email response.

      - The DRF process is absolutely required for the final deployment to Production, but may also be applied to the Pre-Production deployment, i.e. the commencement of the final step of the path-to-production release flow.

Salesforce Summer 13 – Metadata Deployment

Quick post highlighting some Summer ’13 goodness for metadata deployment.

1. Abort a running deployment – This is a massive improvement enabling failed or inadvertent deployments to be cancelled whilst in progress. Anyone working on large deployments will bear witness how frustrating it can be to watch a 30 minute deployment run to completion with a failed unit test occurring after 5..

The Abort option appears against the running deployment in the Salesforce web UI. There doesn’t appear to be an abort operation via the Migration Tool or underlying Metadata API.

2. User references are maintained – Simply put, where individual user references (email alert workflow actions, running users etc.) exist in the metadata, the deployment process attempts to match the source usernames to existing target usernames, by stripping-off sandbox suffixes added to sandbox usernames. This is great for sandbox to production deployments, but needs to work with sandbox to sandbox deployments also – the documentation is unclear on this aspect, although it looks likely that this is supported. Multiple match or non-match cases result in a deployment error. I’ve previously used Post-Retrieve Modification via Ant to deal with this through simple string substitution.

3. Metadata API Supported Types. New additions include Approval Processes and SAML SSO configurations. The former being long overdue as approval processes can be extremely time consuming to recreate manually in a target org. The latter provides a minimal convenience for cases where multiple orgs share the same IdP.

Salesforce Summer 13 – Platform

The Summer ’13 release notes are now available, find below a brief introduction to 10 Force.com highlights (in no order of significance).

1. New Setup interface. Clean new interface for the Setup area, with a top-level “Setup” link in the header region. Administration and Personal settings are now separate, the latter is accessed from a “My Settings” menu option – available on the user name titled menu in the header region. Streamlined and convenient.

2. Record Type assignment via Permission Set. Non-default custom record types can now be assigned via Permission Set, strengthening the concept that Permission Sets encapsulate application permissions.

3. (Universal Single Sign-on) Multiple SSO service provider configurations. A single org can now be configured as a service provider for multiple identity providers. Previously federated SSO required all users to be authenticated by a single IDP, an unrealistic situation where the org services user populations in disparate IT environments.

4. Test methods must be defined in test classes. Anything that enforces good behaviour is a positive step in my mind. A scattering approach to defining test methods can be a maintenance nightmare.

5. Consolidated asynchronous execution limits. Batch Apex, Scheduled Apex and @futures now share a single set of limits calculated as 250k or 200*standard user count per 24 hour period, whichever is greater. This is really great news for orgs with low standard user counts that utilise @future calls (callouts in trigger scripts perhaps). Such orgs have been prone to limit exceptions.

6. System.scheduleBatch(). New Apex method to enable onetime scheduled invocation of batch Apex class. A nice convenience.

7. Sandbox Templates. For full copy sandbox refreshes, template can be defined to specify the objects for which data is copied.

8. Sandbox refresh copies Custom Settings data. This is a long awaited improvement, applies to all sandbox types. Additionally the new Sandbox page UI shows the availability of the different sandbox types.

9. Domain Management. Force.com Sites (and Site.com sites) can now share domains – Custom URLs are defined between the Site and the Domain enabling a many-to-many relationship. For example it is now possible for a custom domain to host a mixture of sites each with a different Custom URL. A new page “Setup>Administer>Domain Management” enables centralised management of domains and custom URLs.

10. Approval Process deployment via Changeset. Ideally this would also be the case with the Metadata API, that said anyway to automate deployment is great news. The manual configuration of Approval Processes can be a time expensive process, and historically has been one of the issues detracting from the value of deployment automation.

Salesforce Summer 13 – Checkpoints

My first post on this blog back in March 2012 related to Simulated Breakpoints, a developer console feature enabling a head dump to be captured when code execution hit a specified line(s) of Apex script. Whilst not comparable to the power of breakpoints in debugging with other languages, Simulated Breakpoints was a definitely step forward for Force.com development, but I suspect this still remains an unused feature, with System.debug() statements being used instead. I don’t believe too many developers are juggling the Force.com IDE and Developer Console, which is unfortunate as the latter provides features and metrics not supported by the IDE.

Checkpoints.
In Summer ’13 Simulated Breakpoints are now termed Checkpoints and can be set on lines of Apex script in the same way using the code editor (now with syntax highlighting) within the Developer Console. In addition to capturing a heap dump, Apex script or SOQL query Execution Actions can be added which run when code execution hits the Checkpoint. Very useful in determining state of the execution context and in particularly in debugging data related issues.

In the screenshot below we can see the new Developer Console UI for Summer 13, the Checkpoints tab and the definition of an example SOQL query Execution Action which will run when the Checkpoint is hit.

3.Checkpoint create

In the screenshot below we can see the result, when the Heap Dump log statement is double-clicked a Checkpoint tab is revealed with subtabs that show the Heap Dump itself, plus the result of defined Execution Actions. Note, I was unable to open the Checkpoint results where an Apex Execution Action was defined, from the log activity it does appear to run as an Execute Anonymous block, but it isn’t clear if the user context is the debugging user or the running user.

4.Checkpoint result

Salesforce Summer 13 – Chatter Publisher Actions

In the first of a series of short posts looking at interesting aspects of the forthcoming Summer 13 release, this posts explores a new capability to add custom publisher actions to the Chatter UI.

In the screenshot below we see the end-result, i.e. a custom publisher action “Create Account” and the UI displayed when invoked.

1.End Result

Publisher actions (or Global Actions) come in 2 forms; Create a Record and Custom Action.

2.Global Action Setup

Create a Record – pick an object, define a layout. Fields can be made mandatory on the layout and given Predefined Field Values, set via formula expressions.
Custom Action – pick a Visualforce page and define the display height dimension

The defined layout or Visualforce page appears as per the first screenshot, in a revealed section beneath the publisher action toolbar. So small, simple displays and basic interactions are in order.

The publisher action toolbar is controlled by Global Publisher Layouts which can be assigned per-profile, and optionally overridden in object page layouts, as required. The Global Publisher Layout is the only means to control the actions visible on the Home page, Chatter Home page and User profile page.

Note, the Enable Publisher Actions option in Chatter Settings must be enabled.

I really like this new feature. The ability to add custom functionality to the Chatter UI opens up a new category of use cases where business process interactions are supported without leaving the Chatter feed. Collaboration really is at the heart of everything.

External Id Deployment Error

On occasion when deploying components between orgs you may encounter a database-level category of deployment error, bubbling up from the underlying Oracle RDBMS with limited information to support any diagnostic process. The example below is one such case I’ve seen in practice recently. Most concerning about this type of error is that the deployment appears to commit (with the exception of the affected fields) but reports a failure result state. It’s therefore one to take seriously in automated build environments where undesirable action may be taken on the basis of the build result etc.

Example Case – Too many external Ids on a single object
Scenario – object (standard or custom) has 3 custom fields defined as external Ids (this is the soft limit), and an attempt is made to deploy a new object definition with 2 different fields marked as external Ids, making the sum 5. Remember, the deployment won’t remove this field attribute. In all such cases, where the updated object definition has more than the 3 limit, the result is..

C:\Program Files (x86)\Jenkins\jobs\..\workspace\build.xml:41: Failed to process the request successfully. Cause(LIMIT_EXCEEDED): LIMIT_EXCEEDED: java.sql.SQLException: ORA-20526: 
ORA-06512: at "HAPPY.CCUSTOMINDEX", line 275
ORA-06512: at line 1
: {call cCustomIndex.insert_detail(?,?,?,?,?,?,?,?,?,?,?)}

..or..

C:\Program Files (x86)\Jenkins\jobs\..\build.xml:41: Failed to process the request successfully. Cause(LIMIT_EXCEEDED): LIMIT_EXCEEDED: java.sql.SQLException: ORA-20526: 
ORA-06512: at "SLEEPY.CCUSTOMINDEX", line 275
ORA-06512: at line 1
: {call cCustomIndex.insert_detail(?,?,?,?,?,?,?,?,?,?,?)}

I’ve also seen this behaviour with deployments that exceed the tracked fields limit in a similar way, however I’ve been unable to recreate this with the 27.0 Metadata API.

Spanning Relationships Limit

Experienced Salesforce technical architects will always look to declarative solution options before considering technical alternatives. This type of thinking is best practice and indicative of an architect considerate of TCO (total cost of ownership), future maintenance etc.. In my case I’ll go as far as to challenge requirements such that I can deliver a fit for purpose solution using declarative features. In the main this is exactly the right approach – but in some cases there’s more to consider. Let me explain.

Large Data Volumes (LDV) is currently a hot topic within the Salesforce technical community, there’s some great resources and prescriptive guidance available. But aside from the data aspect, what about orgs with large and complex declarative and technical customisations? In some cases such LCOs (Large Customisation Org) have grown organically, in others the org is being used as a platform to deliver non-CRM functionality such as complex portal solutions, ERP etc.. In either case what we’re talking about here is an org with high levels of custom objects, workflow rules, formula fields, Apex script and so on. In such a scenario it is highly likely that the LCO will be constrained by capacity limits (maximum number or users, custom objects, data size etc.) or execution limits (governor limits applied to Apex scripts etc.). In the organic growth case, where an org may have started life in one business division and then expanded across the enterprise, there will certainly come a point where a multi-org strategy becomes the only option, continual refactoring and streamlining to provide additional headroom will eventually cease to be viable. In light of this multiple-org architectures are becoming more commonplace with enterprises partitioning over organisation structure or business process boundaries, enabling localised innovation and growth with some data sharing and consolidation. That said, the transition from the single-org to the multiple-org model is potentially costly and disruptive, as such the design factors to consider, to optimise the longevity of a single-org implementation in the face of organic growth, are key for an architect to understand and implement from the outset. A firm understanding of the applicable limits for the org type and user licensing model is the best starting point for this, combined with the practical experience of where limits are soft, and can be increased by salesforce.com support, and where limits are hard platform constraints. This latter type of limit being most relevant to the goal of optimising org longevity. An example being the Spanning Relationships limit.

Spanning Relationships Limit
This limit constrains the total number of unique object relationships which can be referenced in declarative build elements (workflow rules, validation rules etc.) associated with a single object. This is a significant constraint on larger data models, and typically surfaces first for the central standard objects (Account, Contact, Case etc.). The soft limit here is 10, the hard limit being 15, however there are also performance degradation considerations at anything over the 10 level. When this capacity limit is reached, the only options are to refactor the declarative implementation or revert to Apex script solution options. It is therefore critical to understand that this hard limit exists when designing a data model and also when adding declarative elements which introduce a new relationship traversal. There may be an argument for some level of denormalisation in the physical data model, it’s generally unlikely that a Salesforce data model would be in 3NF anyway, unlike a traditional RDBMS a data model optimised for storage is not always the right approach.

Returning back to my original point, in considering declarative solution options versus technical alternatives, the complexity of the data model, plus capacity limits applied to the declarative build model are also factors. There’s no silver bullet answer.

Salesforce Exception Reports

I think it’s fair to say that consideration of reporting and analytics is not traditionally a technical architect’s direct concern. In the Salesforce context I always challenge this presumption and promote the idea that a project architect should absolutely be looking at the high-level analytical requirements during the foundation stage of the project. Why you may well ask? The answer to this is simple, in order to design a fit-for-purpose data model you must consider data coming out (via the native reporting tools) as well as data going in efficiently. I make this point in a prior post that outlines my view on the Salesforce development process. The number of projects I see where this thinking hasn’t been applied and 3rd party tools are wheeled in to address the gap continues to surprise me. Key to success here is understanding what can be achieved with the Salesforce reporting functionality. Which brings me on to the topic for this post – exception reports, a type of reporting not obviously catered for by the standard reporting tools. For instance – show me a list of Accounts without an Opportunity in the last year. Such reports are invaluable in identifying omissions, focusing sales activities, generating call lists, tracking inactivity etc. etc.

I’ll use 2 examples below to clarify how to approach such a scenario.

Simple Example.
Accounts without a closed Opportunity in the last 30 days.

For this report we use a “without” Cross Filter as per the screenshots. Cross Filters were added in Spring ’12 and enable records to be filtered based on the existence (or non-existence) of related records which meet defined criteria. Within a Cross filter up to 5 sub-filters can be defined. Note, the report’s top level filters do not apply at the sub-filter level. Cross filters can be added for any related object, custom or standard – the limit is 3 per report (each with up to 5 sub-filters). Powerful functionality indeed and a great convenience that avoids a Custom Report Type for the outer join, note the sub-filter is limited to fields on the related object, no-cross object sub-filter logic is possible.

simple exception report

Complex Example.
Accounts without a Won Opportunity involving a certain Product Family in the last 60 days.

For this report we start with a Cross Filter report to satisfy the “without Won Opportunity” requirement. From there we need a indicator (of the existence of an Opportunity Product linked to a product of the specified family) at the Opportunity level which can be used in a sub-filter. This is actually straightforward to do and relies on the old school technique for exception reports, namely the use of a rollup summary fields and simple workflow.

Solution components :
Opportunity Product – Custom Field – ProductFamilyHardwareFlag
Number (18,0 default 0)

Opportunity Product – Workflow Rule – “Opp Product – New or Changed Product”
Evaluate – Created and Edited – Formula (OR(ISNEW(), ISCHANGED( Product2Id )))

Opportunity Product – Workflow Action – “Opp Product – Set ProdFamilyHardwareFlag”
Field to update [ProductFamilyHardwareFlag] with Formula = IF( TEXT(Product2.Family)=’Hardware’, 1,0)

Opportunity – Custom Field – ProductFamilyHardwareCount
Rollup Summary Field (Opportunity Product – SUM – Field to aggregate = ProductFamilyHardwareFlag). All records included.

How does this all work? In short, a workflow rule on Opportunity Product sets a numeric field equal to 1/0 based on the product family meeting the defined criteria. The numeric field is then rolled up to the Opportunity level via RSF, which is then applied in a sub-filter as below.

complex exception report

Note, this is just an example to illustrate the concept. An actual implementation would typically require a more generic approach.

UI Tips and Tricks – Inline Visualforce Resize

A real frustration with inline Visualforce pages (added to standard page layouts) is the static nature of the height setting. From the layout we can set a specific height, but ideally we want the height set dynamically from the content height. Sounds like a simple enough requirement, however, the fact that the VF page section is implemented as an iFrame loaded from another domain makes cross-domain communication a non-trivial task. Note, for security reasons browsers enforce a same-origin policy, preventing script running across domain boundaries. Workarounds to this restriction include the HTML5 postMessage function on the client and proxy services on the server. So the question becomes, how can the iFrame content communicate across domains to tell the host page the correct height for the iFrame? The answer to this is somewhat contrived, but hopefully my basic approach below tells a clear enough story.

Here we go.
1. The inline VF page contains an iFrame, into which we load a helper script file from the base salesforce domain with a height parameter in the querystring.

document.getElementById(‘helpframe’).src=’https://emea.salesforce.com{!$Resource.iFrameHelper}?height=’+h+’&iframename=MYPAGENAME&cacheb=’+Math.random();

The random parameter is there to avoid caching issues. Crucially as this helper script is running on the same domain as the standard page layout, it can call a script in the page itself. Note the helper script is loaded from a static resource. To keep the solution generic the page name is passed as a parameter also, handily the title attribute in the host page is set to the page name, we’ll use this later to find the id for the iFrame.

2. In the helper script we extract the 2 parameters from the querystring and call a script function in the host page (via parent.parent – which traverses up the DOM to the parent page).

3. In order to add script to the host page we use the Sidebar injection technique (or hack) and introduce a simple Javascript function (via a narrow component) which takes the page name and height, finding the former in the DOM using Ext.query (Ext is already referenced in the page), and setting the element height to the latter.

Example solution components::

0. Pre-requisites:
User Profiles must have the “Show Custom Sidebar On All Pages” General User Permission ticked.

1. Add a HTML file static resource, named iFrameHelper – content below.

<html>
  <body onload="parentIframeResize()">  
    <script>  
      // Tell the parent iframe what height the iframe needs to be 
      function parentIframeResize(){ 
         var height = getParam('height'); 
         var iframename = getParam('iframename');
         // This works as our parent's parent is on our domain.. 
         parent.parent.resizeIframe(height,iframename); 
      }  
      // Helper function, parse param from request string 
      function getParam(name){ 
        name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]"); 
        var regexS = "[\\?&]"+name+"=([^&#]*)"; 
        var regex = new RegExp( regexS ); 
        var results = regex.exec( window.location.href ); 
        if( results == null ) 
          return ""; 
        else 
          return results[1]; 
      }
    </script>  
  </body>  
</html>

2. Add a HTML sidebar component (narrow left) – click “Show HTML” and paste in markup below.

<script>
  function resizeIframe(h, ifn){
   var e = Ext.query("iframe[title='"+ifn+"']");  
   console.log(e); 
   var itarget = e[0].getAttribute('id'); 
   Ext.get(itarget).set({height: parseInt(h)+10});
  }
</script>

3. Add a Visualforce page named MyTestInlineVFPage – paste in markup below.

<apex:page docType="html-5.0" standardController="Account">
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  This is your new Page<br/>
  
  <script type="text/javascript">      
        function resizeParentIFrame(){
            var h = document.body.scrollHeight;
            //TODO - replace with the relevant page name.
            var iframename = 'MyTestInlineVFPage';
            //TODO - replace with the relevant base url - page runs on the VF domain so functions return the VF domain. 
            var baseUrlForInstance = 'https://emea.salesforce.com';
            document.getElementById('helpframe').src = baseUrlForInstance+'{!$Resource.iFrameHelper}?height='+h+'&iframename='+iframename+'&cacheb='+Math.random();
        }
        
        function forceParentIFrameResize(){
            document.getElementById('helpframe').src=document.getElementById('helpframe').src;
        }
        
        window.onload=function(){
            resizeParentIFrame();
        }
  </script>  
</apex:page>

4. Add the VF page to a new section on an Account layout.

The solution above needs further work in the areas below. I’m planning to improve this as part of a commercial AppExchange package I’m working on and will post the improved resize solution.

Code quality.
Exception handling.
Calculation of the base salesforce domain – currently hardcoded in the inline page.

I’d be delighted to hear about other improvements, or indeed alternative approaches.

Salesforce Ant Scripts – Selenium

The Salesforce metadata API is an extremely powerful tool, when combined with Ant, Jenkins etc. for build automation. There is however a number of configuration items that simply can’t be retrieved and deployed using this API (Account Teams, Support Settings, Lead Settings, Case Assignment and Escalation Rules etc.). The unsupported list can be found here, unfortunately the platform expands at a rate more or less equal to the rate at which coverage of the API has increased over time. Anyway, my point here is that typically deployments have three steps; a manual step to cover the gaps in the metadata API (pre-requisites), an automated deployment step (retrieve-and-deploy with Ant) and finally a data population step (Data Loader CLI with Ant perhaps..). Leaving data to one side (for this post), an ability to merge steps 1 and 2 would enable full automation of the deployment of configuration – which in most cases would be a good thing. One approach to automate step 1 is to write Selenium web browser automation scripts which drive the Salesforce application at the UI level. The scripts can be exported as JUnit test cases and then be incorporated into an Ant based build process and automated. My approach to doing this is outlined below, as with most things there are many ways to achieve the same result and I’m sure this can be improved on, however it keeps the process simple and gets the job done which tends to work for me. Additionally, the approach plays well with Ant, Jenkins/Hudson etc.. so it should be straightforward to extend an existing build process.

1. Install the Selenium IDE Firefox Extension.
2. Using Selenium IDE record the act of logging-in to Salesforce and making the required configuration changes.
3. Export the test case as a Java / JUnit 4 / WebDriver file. This creates a .java file as below. The example simply creates a Chatter post for the logged-in user, hopefully this is simple and illustrative enough to make the point.

package com.example.tests;

import java.util.regex.Pattern;
import java.util.concurrent.TimeUnit;
import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import org.openqa.selenium.*;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.Select;

public class SeleniumTest {
  private WebDriver driver;
  private String baseUrl;
  private boolean acceptNextAlert = true;
  private StringBuffer verificationErrors = new StringBuffer();

  @Before
  public void setUp() throws Exception {
    driver = new FirefoxDriver();
    baseUrl = "https://test.salesforce.com/";
    driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
  }

  @Test
  public void testSelenium() throws Exception {
    driver.get(baseUrl + "/");
    driver.findElement(By.id("username")).clear();
    driver.findElement(By.id("username")).sendKeys("release.manager@force365.com");
    driver.findElement(By.id("password")).clear();
    driver.findElement(By.id("password")).sendKeys("mypassword");
    driver.findElement(By.id("Login")).click();
    driver.findElement(By.id("publishereditablearea")).clear();
    driver.findElement(By.id("publishereditablearea")).sendKeys("new Chatter post - Selenium");
    driver.findElement(By.id("publishersharebutton")).click();
  }

  @After
  public void tearDown() throws Exception {
    driver.quit();
    String verificationErrorString = verificationErrors.toString();
    if (!"".equals(verificationErrorString)) {
      fail(verificationErrorString);
    }
  }

  private boolean isElementPresent(By by) {
    try {
      driver.findElement(by);
      return true;
    } catch (NoSuchElementException e) {
      return false;
    }
  }

  private String closeAlertAndGetItsText() {
    try {
      Alert alert = driver.switchTo().alert();
      if (acceptNextAlert) {
        alert.accept();
      } else {
        alert.dismiss();
      }
      return alert.getText();
    } finally {
      acceptNextAlert = true;
    }
  }
}

4. Modify the test case java code as required.
5. Download the Java Selenium Client Driver from http://seleniumhq.org/download/
6. Extend or create a new Ant build file to compile and execute the test case. My example below requires a [selenium\src] sub directory structure in the build root, with the .java test case files placed in the src directory.

<project basedir="." default="usage" name="invoke Selenium script to configure Salesforce org">
	<property name="bin" value=".\selenium\bin" />
	<property name="lib" value="c:\Release Management\selenium-2.28.0\libs" />
	<property name="src" value=".\selenium\src" />
	<property name="report" value=".\selenium\reports" />	
	
 	<target name="usage" depends="">
		<echo message="Compiles and executes Selenium IDE exported test cases (source format JUnit4 WebDriver .java files)" />
	</target>

	<target name="init">
		<delete dir="${bin}" />
		<mkdir dir="${bin}" />
	</target>

	<target name="compile" depends="init">
		<javac includeantruntime="false" source="1.7" srcdir="${src}" fork="true" destdir="${bin}" >
			<!-- requires Selenium test cases exported as JUnit4 WebDriver .java files in the src sub-directory -->
			<classpath>
		    	<pathelement path="${bin}">
		        </pathelement>
		        <fileset dir="${lib}">
		        	<include name="**/*.jar" />
		        </fileset>
		  	</classpath>
		</javac>
	</target>

	<target name="exec" depends="compile">
		<delete dir="${report}" />
		<mkdir dir="${report}" />
		<mkdir dir="${report}/xml" />
		
		<junit printsummary="yes" haltonfailure="yes">
			<classpath>
		    	<pathelement path="${bin}">
		    	</pathelement>
		        <fileset dir="${lib}">
		        	<include name="**/*.jar" />
		        </fileset>
		  	</classpath>
			<test name="com.example.tests.SeleniumTest" haltonfailure="yes" todir="${report}/xml" outfile="SeleniumTest-result">
				<formatter type="xml" />
			</test>
		</junit>
		
		<junitreport todir="${report}">
	        <fileset dir="${report}/xml">
	            <include name="TEST*.xml" />
	        </fileset>
	        <report format="frames" todir="${report}/html" />
	    </junitreport>		
	</target>
</project>

Note. There is no need to start or stop a Selenium server as the script runs locally on the build server – Firefox will be required however if you stick with the default browser in recorded scripts.

I’ll follow this initial post with further detail on the following;
1. Conditional script logic – i.e. I want the script to check for a condition before making a change such that it selectively configures and therefore won’t be reliant on a clean, predictable state.
2. Execution of test suites rather than individual cases.
3. Most likely I’ll refine the build.xml example as I understand more about this.