One of my clients recently switched hosting providers which threw me for a loop because I was doing all their web site deployment using Subversion.  I'd make a change on my development box, commit it, ssh over to their host and issue a matching svn update.  It worked great and gave me a big warm fuzzy.

The new host sadly doesn't have svn installed and while I could probably bug them to install it for me I thought I'd see what others out there were doing when it came to shared hosting.  Seems svn+rsync is a popular choice along with variations on that theme but I wanted something simpler.  Enter Springloops.

Springloops is basically a hosted svn repository that will push your changes via FTP to a deployment server, but that doesn't truly convey just how smooth an experience it really is.  Getting set up is a snap, the interface is well thought out and has a great aesthetic.  You know there is a svn repository behind the interface but it's presented in a very non-threatening for the non-nerd way.

Once you've imported all your files, using whatever svn client you like, you setup a deployment server, giving it your FTP host, path and log in information and from that point on deployment is as simple as a button click.  All of this for free and if you step up to one of their paid plans you get automatic deployment, large storage, more deployment servers and more depending on the level you pick.

All in all I actually like this solution better than what I had before because it's easier for others to interact with your site as well as push deployments.  Anyone looking for a way to use version control (you are using version control, right?) with shared hosting should definitely give Springloops a try.

0 Comments

At the end of last year I picked up HP's MediaSmart Server because while playing the three computer Monte at home I managed to lose 10 years worth of documents which really shook me up.  It's a small, energy-efficient, quiet little box running Windows Home Server and frankly it rocks.  It automatically backs up my laptop, my desktop and my wife's laptop fully every night and it's pants-wearing-monkey simple to get setup.

I bought the 500GB version but decided I also wanted to move my entire video and music collection over to WHS so last week I picked up another 500GB drive from Amazon (via Giveness) and it arrived yesterday so last night I set about installing the puppy and suffice to say I have never had such an amazing experience installing any piece of hardware other than say an USB flash drive.

Installing a new internal drive is as simple as swinging open the front cover, sliding out a tray, popping the new drive into it and sliding it back in, all while the computer is still running.  After closing the front cover I got a little prompt that new storage was available and asking if it should include it in it's "cloud" of storage.  It was that simple.  It took me longer to unpack the drive than to install it and I have every confidence that I could have easily walked my wife, mother or 2 year-old nephew through the same process.

On a site note, a lot of people give WHS a big "meh", saying you can easily duplicate all that functionality with free software and while that's entirely true I highly doubt it can be reproduced as easily or cheaply. 

By easy I mean that I have confidence that even households without a power geek could quickly setup it up out of the box and that my wife doesn't need me around to do a complete restore or to pick individual files out of an old backup. 

By cheap I mean that my time is money, every hour I spend fiddling with a backup server is an hour I'm not writing new and interesting code, spending with my wife or improving my XBox Live Gamerscore.  Once WHS is running I don't have to do anything, no maintenance or patching since Windows Update takes care of all that.  No monthly fee to a backup provider like Jungle Disk (though if you don't have a backup server I suggest you look at one of the S3-backed providers like Jungle Disk).  No time wasted as I try to pull down a 4 gig image via the wire.

I continue to be impressed by WHS and the ability to hot-swap a new drive just bumped it up even further in my opinion.  The fact that it's such a great product at 1.0 means it can only get better from here.  In the future I'd like to see better integration with Media Center and I'm seeing all kinds of options if you combined it with Live Mesh.

For any household with multiple computers with data you just don't want to lose I can't recommend WHS enough.

2 Comments

I recently moved my blog from blogger.com to GoDaddy with BlogEngine.NET as my blog engine and so far everything has been pretty smooth once I figured out how to move all my old posts from blogger into BlogEngine.NET.  Once I got a basic template up and most of the data over I considered it "good enough" but it's always bothered me that I have duplicate data hanging around out there so today I rolled up my sleeves.

I really hate broken links so didn't want to pull my old blog entirely incase anyone was linked to it but I did want them redirected to the new hotness and so after some searching I came across a great link, How To Redirect Blogger Beta To Wordpress.com, and that got me about 90% of the way there.  I only ran into a few issues.

The first issue was that his script assumes a Wordpress permalink format (obviously, based on the post) so I had to adjust the regex to BlogEngine.NET style.  After some tweaking and having to once again remember regex I got that working which led me into my next issue.

The second problem was that blogger doesn't use the entire post title for the slug, it stops at a certain character limit whereas BlogEngine.NET uses the full Monty.  I didn't have a magic fix for this so instead I fired up Google Analytics, looked at my top 10 posts and manually adjusted the slug in BlogEngine to match what was coming through blogger and now the redirects come over like butta.

Another minor issue was that the auto-generated sitemap has a lastmod date of 0001-01-01, which really pisses web crawlers off.  At first I thought it was just a few posts and was manually updating them but then realized it was pretty much everything.  I'll probably spin through the posts and set the last modified to the post date but I was a little disappointed that BlogEngine.NET didn't have this logic already built in.

All in all I've been very happy with BlogEngine.NET and I like knowing I can start mucking up the code if I feel really creative.  For another perspective on switching check out Steve Trefethen's thoughts on BlogEngine.NET.

1 Comments

A few weeks ago Rob Conery foolishly tapped me to help get migrations in SubSonic up to snuff and I've been working on them ever since trying to sneak them into the latest SubSonic beta.  I've changed the way they're implemented slightly from when Rob first talked about them so here's a quick re-introduction to migrations.

Migrations are a way to create and version your database schema using code rather than having to rely on SQL scripts or compare and sync tools.  They allow you to quickly rollback schema changes as well migrate schema changes from your development database to staging and then on to production.  In a nutshell they rock when it comes to database maintaince, versioning and deployment.

Migration Breakdown

A migration is a class that descends from SubSonic.Migration and overrides both the Up() and Down() methods.  Up() is used when going up a version and Down() is used to restore the database schema to the pre-Up() state.  Anything you do in the Up() should be undone in your Down(). 

By convention they are put in a Migrations folder off the root of your project folder.  While the actual name of the class isn't important the name of the file is critical because this is how SubSonic determines which version the migration represents.  The naming convention is 000_MigrationName.cs (or .vb) with the version number represented by leading three numerics, starting at '001' and working your way up.  Currently it's pretty particular about that naming convention so make sure it's exactly three numerics, padded with zeros if needed.  It's convention to name your migration file something descriptive and to also not repeat names, such as:

001_AddExerciseTable.cs

An Example

Let's start with a simple example and break it down:

using System;
using System.Collections.Generic;
using System.Text;
using SubSonic;

namespace SubSonic {
  public class Migration001:Migration {

    public override void Up() {
      TableSchema.Table t = CreateTable("Flights");
      t.AddColumn("Name", System.Data.DbType.String);
      t.AddColumn("FlightNumber", System.Data.DbType.String, 100);
      t.AddColumn("DateTraveling", System.Data.DbType.DateTime, 0, false, "getdate()"); 
      AddSubSonicStateColumns(t);
    }
  
    public override void Down() {
      DropTable("Flights");
    }
  }
}

In the example the 'Flights' table is created, then three columns are added to it, followed by the standard SubSonic state columns.  If you don't specify a primary key one will be created for you with the pattern of 'TableNameID', so that's one less thing to worry about.  The Down() method undoes everything we did in the Up() by dropping the 'Flights' table.

Available Methods

Currently the methods available from inside your migration are:

  • CreateTable(string tableName) - This creates and returns a table schema to which you can add your columns, as seen in the example.
  • DropTable(string tableName) - Does exactly what it says.  If your Up() has a CreateTable() you'll need a corresponding DropTable().
  • AddColumn(string tableName, string columnName, ...) - Used to add a new column to an existing table.  It has all the same overloads as TableSchema.Table.AddColumn() except the first parameter is the name of the table you'll be adding columns to.
  • RemoveColumn(string tableName, string columnName) - You only get one guess that this does :)
  • AlterColumn(string tableName, string columnName, ...) - Used to alter an existing column, again, the same overloads as AddColumn.
  • AddSubSonicStateColumns(TableSchema.Table table) - Adds the conventional SubSonic state columns to your table.  I'll be adding another overload that just takes a tableName if you want to add those columns to an existing table.

Running your Migrations

To run your migrations you'll use SubCommander, the same tool used to generate your models but with the 'migrate' command.  The simplest usage is:

sonic migrate

That's it.  It'll use your default provider, look for your migrations in <project>\Migrations and run every migration Up() starting at your database's current migration up the last one found in the Migrations folder.  You can also specify the provider, migration directory and version at the command line like this:

sonic migrate /provider "Northwind" /migrationDirectory "D:\Testing\Migrations" /version 4

A few things to remember:

  • Migrations by convention are looked for in a \Migrations folder off the root of your project, though this can be changed via the command line. (/migrationDirectory "D:\Migrations")
  • You run a migration against a single provider at a time, there is no support for specifying the provider inside the migration.  The main reason is portability, often you'll be running this migrations against different databases and hardcoding the provider name in the migration destroys their usefulness.
  • Migrations will run against the default provider unless otherwise specified via the command line (/provider "Northwind")
  • By default migrations will try to go up to the latest version found in the migrations folder.   To go up or down to a specific version use /version X to indicate which version.
  • To enable migration support a new table 'SubSonicSchemaInfo' will be created in your database, so don't delete it and tell your DBA that it's OK :)

TODO

These are things you *should* see before the next beta drop, but don't hold me to it :)

  • Ability to generate your migration code skeleton using sonic.exe.
  • Add RenameTable()
  • Add RenameColumn()
  • Add ability to execute ad-hoc sql, for creating stored procs, views, creating roles, users, etc.
  • Add constraints
  • Add foreign keys

2 Comments

Designed by Free CSS Templates. | Sign in