views:

130

answers:

6

What method should I use to determine if I'm on the dev system vs. production? In this post from Ray Camden, he shows how to see what folder you're in, so that could be an indicator.

While in dev, I want to have error trapping turned off, missing template turned off, debug="yes" for cfstoredproc and cfquery, as well as always reload the components onRequestStart.

A: 

Is it possible to get the directory of the currently running application?


Consider this directory structure for the different "instances" of your application:

/home/deploy/DevLevel.0/MyApp
Production Version

/home/deploy/DevLevel.1/MyApp
Preview or Staging Version

/home/deploy/DevLevel.2/MyApp
Development Version


If you can read the path to the current application, it's easy to find the integer after DevLevel. With that in hand (set as a global variable/constant), use it to change settings or behavior at runtime:

DevLevel == 0 means "Production"
DevLevel >= 1 means "Development"

For example, in the credit card authorization code:

if(DevLevel > 0)
    enable_test_mode();

In error handling code:

if(DevLevel == 0)
   send_error_to_log();
else
   print_error();

Conclusion

The primary benefit here is that the code between the versions can remain 100% identical . No more "forgetting to enable this or disable that when moving code live".

Can this be implemented in ColdFusion?

gahooa
Yes, it can be done in ColdFusion. On our dev server, everything is under the /Projects subfolder, so the existence of the a /Projects root would tell me if I'm on dev or production. I guess my question is: what's the best way to determine it. Should I have an xml file outside of the web root that sets each of the criteria, such as logging, debugging, error trapping, formatting. Or should I just have a binary criteria of dev vs. production.There may be times while in production that I want to turn error trapping off.
cf_PhillipSenn
And if I put it in an xml file, does that mean I read the xml file onRequestStart? Probably not. If I have an Application scoped variables like Application.ErrorTrapping, Application.Logging, Application.Debugging, Application.Reinit, then if they are already set I don't need to reread the config file onRequestStart.
cf_PhillipSenn
+1  A: 

Can you just enable debugging in CFAdmin on your Dev box for your IP then use IsDebugMode()?

kevink
Brilliant!But wait: I've already got debugging on and IsDebugMode() is returning "NO".
cf_PhillipSenn
Are you sure your IP is allowed and you have no <cfsetting enabledbuggingoutput="no> tag in effect?
kevink
+1  A: 

Dump the #server# scope and you'll see some keys that may help - eg the license mode of ColdFusion.

Antony
Maybe. Not sure what the logic would be.
cf_PhillipSenn
+3  A: 

I have two approaches to this, both of which have served well. I'll start with the easiest approach first, which is what I'd call a "static". I use this when I don't have many environment-specific settings... maybe a small handful.

I'm assuming you have an Application.cfc or .cfm file for your app. In there, you could set a variable, something like "application.environment", and by default it'd be set to "dev". Throughout your app you could inspect that variable to determine where you are.

When you package your application for deployment, you could then change that Application.cfc file to read "" instead.

Now, that's going to get annoying, so I just use ant for this. I just use something like this in my build.xml, which lives in the same directory as Application.cfc:

<replace file="Application.cfc" token="DEV" value="PROD" casesensitive="true" />

And then zip the app for deployment:

<zip destfile="${zipdir}/MyApp-Production.zip">
<zipfileset dir="." prefix="MyApp" />   
  </zip>

Then I deploy the zip. If I'm working on a small project that uses FTP instead of some corporate enterprisey deployment hooey, then I'll just have an ANT task that FTPs files to my production server and it'll also perform that replace on Application.cfc and push that file, too.

For most of the apps I work on where I work, we use two database tables to manage environments. We do this because we have a lot of different environments, and each one has different settings, usually centered around filesystem and network paths that differ per environment (let's not talk about why they're different... totally separate discussion). So We have a table we call "AppLocations":

LocationID | LocName | LocDesc | Setting1 | Setting2 | Setting 3| ...... 1 | Local | 'Localhost Environment' | whatever..... 2 | Dev | 'Development Environment' | whatever.... 3 | Test | 'Test Environment' | whatever.....

and so on.

Then, we have another table named "AppLocationHosts"

LocationID | LocHostName 1 | 'localhost' 2 | 'devservername' 2 | 'otherdevservername' 3 | 'testservername' 3 | 'othertestserver'

and so on.

then, in Application.cfc, in onApplicationStart, we do this query

SELECT TOP 1 *
  FROM AppLocations
  WHERE LocationID IN (SELECT LocationID FROM AppLocationHosts WHERE LocHostName =  <cfqueryparam value="#CGI.HTTP_HOST#" cfsqltype="cf_sql_varchar"/>)

And from there, once we know what location we're in based on the http_host match, we set those "Setting" columns into the application scope:

<cfloop list="#qryAppPathLocations.ColumnList#" index="ColName">
  <cfset application[ColName] = qryAppPathLocations[ColName]>
 </cfloop>

This approach isn't for everyone, but in our weird environment where consistency is unusual, it's been a very flexible approach.

Now, if you literally only have two environments, and one of them is "localhost" and the other is "www.myapp.com", then by far the easiest would be to just do a check on http_host in onApplicationStart and if you're in "www.myapp.com", then you do your production-specific setup. Perhaps here you set stuff like "request.querydebug = true" and then when you're in production, you turn that off. Then your queries could use that flag to determine whether to turn debug on or off for the cfstoredproc and query. Though I must say, I strongly recommend against that.

marc esher
Wow Marc. What a considered response.There's a lot to think about here. I don't think I like the idea of using the cgi scope.I wish build.xml were outside the project's folder, so that dev and production could potentially be the same.Going to the database might be one option because I go to the database already to see if it's been rebuilt since the last onrequeststart. I don't know why I didn't think of that before.
cf_PhillipSenn
Phil, you can keep your build files anywhere you want. In my example above I had it in the project and consequently the zipfileset dir=".", because "." means "this directory". But you could just as easily do <zipfileset dir="c:\apps\myapp\" .../>, i.e. just point dir to whatever directory you want to zip.
marc esher
+1  A: 

The solution we use is to set the IP of the current instance, and check it against our known "dev" IPs. Simple, easy, works.

dhorn
That's not a bad idea.
cf_PhillipSenn
A: 

A lot of good answers here - I'd like to mention using cgi.server_name , which can be combined with using a custom DNS to specify your dev environment. To get the localhost working, for IIS on Windows, set up hosts file like e.g. this:

C:\Windows\System32\drivers\etc\hosts - add entry: 127.0.0.1 myapp.dev.mydomain.com.au

Then in IIS map your server to this DNS.

Your systest and uat servers might be set up properly in your corp's DNS, such as myapp.systest.mydomain.com.au - systest myapp.uat.mydomain.com.au - uat myapp.mydomain.com.au - production

Then, in my application.cfc I have a getEnvironment() that is called on every load for ease of use:

// get the environment based on cgi variables - top of application.cfc
this.stConfig = THIS.getEnvironment();

//... onApplicationStart

if (!stConfig.validEnvironment) {
    writeOutput("Environment #cgi.server_name# not recognised");
    return false;
}

// ...
public struct function getEnvironment () {
    stConfig=structnew();
    stConfig.validEnvironment = 1;

    switch (cgi.server_name) {
        // my dev environment
        case "myapp.dev.mydomain.com.au": {
            stConfig.env = "dev";
            // +++
        }
        // my dev environment
        case "myapp.systest.mydomain.com.au": {
            stConfig.env = "systest";
            // +++
        }
        // etc
    }
    return stConfig;
}

I will also copy stConfig to the request scope.

Now, I've got a lot of other stuff there too, and there's lots of ways to implement the storage of environments, e.g. but basically I find the combination of DNS and cgi.server_name particularly well suited to managing environments.

Fwiw, I will include ini files in application.cfc based on the environment name that I use for storing environment specific configurations. I find the getProfileSections() very useful for this, as the config files are very easy to work with. I have one common file that is shared between all environments, and then environment specific ones for those settings that need to be tailored to each environment.

JorgenS