Create .NET 4 ClickOnce applications from the command line
28/03/2012 § Leave a Comment
ClickOnce packages have always been complicated to build from the command line with if you want to go beyond msbuild /t:Publish. Given that ClickOnce doesn’t seem as common these days, there’s a dearth of information regarding building .NET 4 applications, so I’ll record some of the issues I encountered in the hope of saving others time.
- Use the correct version of mage.exe: This is located in the Windows SDK (Program Files\MicrosoftSDKs\Windows\7.0A or 7.1) under ‘bin\NETFX 4.0 Tools’. Don’t use the mage.exe directly under bin – this is the 3.5 version and you’ll end up getting “Assembly is incorrectly specified as a file” errors.
- Select ‘Create application without a manifest’ under the executable project properties Application tab: If you embed a manifest ClickOnce seems to get confused and you’ll get “Reference in the manifest does not match the identity of the downloaded assembly”.
- Untick the ‘Enable ClickOnce security settings’ checkbox in the project properties Security tab: This also causes issues if you are building external to Visual Studio.
- Specify the ‘AppCodeBase’ parameter on -New Deployment: I found if the AppCodeBase is not specified, it defaults the value specified by AppManifest but mangles (truncates) it.
- Add .deploy extension to files after -New Application: you’ll need a .deploy extension on the files to serve them from IIS, but if you add this before creating the application manifest, you’ll get “mismatched identity, expected file name: ‘<filename>.deploy>” warnings in the build. You need to create the manifest, change the filename, then re-sign the manifest.
You’ll also need to modify your deployment manifest xml directly – you can’t set all the values via mage.exe (in particular mapFileExtensions). It will need to be re-signed afterwards.
Below is listed a sample psake build script that generates a ClickOnce package. It assumes the correct mage is available on the path, and the certificate doesn’t have a password.
Task Build {
#Build your solution...
$files = "$build_artifacts_dir\Release\MyApp", "$build_artifacts_dir\Release\MyApp.exe.config"
Create-ClickOnce $files -App_Name "MyApp" -Version "1.0.0.0" -Output_Dir "$build_artifacts_dir\ClickOnce" -Cert $certificate -Deployment_Url "http://example.com/MyApp"
}
function Create-ClickOnce {
param($app_name, $version, $output_dir, $cert, $deployment_url)
$files = $args[0]
$version_dir = "$output_dir\$app_name_$($version.Replace(".", "_"))"
mkdir $version_dir
$relative_version_dir = [System.IO.Path]::GetFileName($version_dir)
#Copy files into the output folder and generate the .manifest and .application files.
Copy-Item $files -Destination $version_dir
Exec {mage -New Application -ToFile "$version_dir\$app_name.exe.manifest" -Name $app_name -Version $version -Processor X86 -CertFile "$cert" -FromDirectory $version_dir -TrustLevel FullTrust }
Exec {mage -New Deployment -ToFile "$output_dir\$app_name.application" -Name $app_name -Version $version -Processor X86 -AppManifest "$version_dir\$app_name.exe.manifest" -AppCodeBase "$deployment_url/$relative_version_dir/$app_name.exe.manifest" -CertFile $cert -IncludeProviderURL true -ProviderURL "$deployment_url/$app_name.application" -Install true }
#Append .deploy to files for web server deployment, then re-sign the manifest. No idea why mage can't do this.
Get-ChildItem $version_dir | Foreach-Object { if (-not $_.FullName.EndsWith(".manifest")) { Rename-Item $_.FullName "$($_.FullName).deploy" } }
Exec {mage -Sign "$version_dir\$app_name.exe.manifest" -CertFile $cert }
#Set parameters in deployment xml, then re-sign the deployment. Why can't we do this from the command line, Microsoft?
$xml = [xml](Get-Content "$build_artifacts_dir\ClickOnce\NaplanDB.application")
$deployment_node = $xml.SelectSingleNode("//*[local-name() = 'deployment']")
$deployment_node.SetAttribute("minimumRequiredVersion", $version)
$deployment_node.SetAttribute("mapFileExtensions", "true")
$xml.Save("$output_dir\$app_name.application")
Exec {mage -Sign "$output_dir\$app_name.application" -CertFile $cert }
}
TextMate 2 – recover HTML Output pane
27/01/2012 § Leave a Comment
I accidentally dragged the HTML Output split view down below the bottom of the editing pane in TM2, and couldn’t for the life of me get it back. After trawling through the defaults database with no luck, I eventually found the file ~/Library/Application Support/TextMate/Session/Info.plist which contained the value
htmlOutputHeight = -25;
Changing this file seemed to splat the session and I managed to get my markdown preview back.
Global Day of CodeRetreat
05/12/2011 § Leave a Comment
Most of the team went along to the Perth Global Day of CodeRetreat at the Thoughtworks office on Saturday; I’d been interested in going to one for a while, so when a local event popped up, I was there. Most of the pairs were split 50/50 on Java/C# — conservative bunch we are in Perth — although there were some JavaScript pairs and I participated in an Objective-C one (with limited success).
Overall I found the experience quite worthwhile, but I did find it frustrating to attack the same problem (Conway’s Game of Life) over and over, and never finish it. So I went home and finished my last attempt.
This implementation adheres to most of the constraints we attempted at various stages during our event:
- No control of flow statements (no if, else, switch, do, while, loop, for, foreach etc)
- No Cell class
- Infinite Grid
- Methods no longer than 3 lines of code
It consists of one class and one struct, first a helper Point struct:
public struct Point
{
public int X { get; private set; }
public int Y { get; private set; }
public Point(int x, int y) : this()
{
X = x;
Y = y;
}
public override bool Equals(object obj)
{
return X == ((Point)obj).X && Y == ((Point)obj).Y;
}
public static bool operator == (Point p1, Point p2)
{
return p1.Equals(p2);
}
public static bool operator != (Point p1, Point p2)
{
return !p1.Equals(p2);
}
public override int GetHashCode()
{
return X.GetHashCode() & Y.GetHashCode();
}
public IEnumerable<Point> AdjacentPoints
{
get
{
return new List<Point> { new Point(X - 1, Y - 1),
new Point(X - 1, Y),
new Point(X - 1, Y + 1),
new Point(X, Y - 1),
new Point(X, Y + 1),
new Point(X + 1, Y - 1),
new Point(X + 1, Y),
new Point(X + 1, Y + 1) };
}
}
}
and then the main ‘World’ class:
public class World
{
private List<Point> liveCells;
public World(List<Point> liveCells)
{
this.liveCells = new List<Point>(liveCells);
}
public int LiveCellCount {
get { return liveCells.Count(); }
}
public void Tick()
{
liveCells = liveCells.Where(p => NumberOfLiveNeighbours(p) == 2 || NumberOfLiveNeighbours(p) == 3) // stay alive
.Union(AdjacentDeadCells.Where(p => NumberOfLiveNeighbours(p) == 3)) // become alive
.ToList();
}
private int NumberOfLiveNeighbours(Point point)
{
return liveCells.Intersect(point.AdjacentPoints).Count();
}
private IEnumerable<Point> AdjacentDeadCells
{
get
{
return liveCells.SelectMany(p => p.AdjacentPoints).Except(liveCells).Distinct();
}
}
}
This is cheating slightly as the framework LINQ queries are probably implemented as foreaches, but technically I haven’t used any control of flow. The code is also not as easily testable as some of the iterations I went through during the CodeRetreat, but there is significantly less of it. I think there can be scenarios where attempting to modularise something can introduce too many interfaces to keep the code tight & concise.
Running Azure dev storage without an SQLExpress instance
29/11/2011 § Leave a Comment
If, like me, you think the SQL Express named instance is a blight on humanity and refuse to sully your dev machine with it, you’ll run into issues trying to start up Azure development storage: “Failed to start Development Storage: the SQL Server instance ‘localhost\SQLExpress’ could not be found”.
The instructions here no longer work with SDK 1.5 – there’s no DevelopmentStorage.exe.config. Instead, you’ll need to run
DSInit /sqlinstance:.
(in “Windows Azure SDK\v1.5\bin\devstore”). This performs magic and your dev storage will start working.
Implementing AWS authentication for your own REST API
07/09/2011 § 4 Comments
If you need to build an authentication mechanism for an HTTP-based REST API, a common approach is to use HTTP Basic – it’s simple, all clients have it built-in, it’s easy to test from the browser, and you can store passwords as hashes. The downside is that your credentials are transmitted in (nearly) plain text, which makes SSL (with its associated security restrictions and computational cost) a necessity.
If you’d like to implement a simple scheme for a non-sensitive API that doesn’t require SSL, this is more complicated. HTTP Digest requires storing unhashed passwords on the server, and requires a challenge-response conversation between server & client. Schemes like Kerberos and three-legged oath require hanging your hat on a third-party authentication provider, and are awkward to implement in a client.
Luckily, in software if you hit a problem you can usually copy somebody else’s solution. This is what the Microsoft Azure team did when implementing their API authentication – “Let’s just copy what Amazon does“. Amazon probably copied someone else. Who am I to argue with that approach?
The general concept behind these schemes is relatively simple:
- Come up with a way to generate API & secret keys for a client. These are usually base64-encoded cryptographically generated random byte arrays.
- For each request, hash a string containing the requested URL and specific headers (including the Date header) with the secret key using HMAC-SHA1.
- Add an Authorize header with a custom scheme name (e.g. ‘AWS’), containing the access key & base64-encoded signature separated by a colon.
The client goes through this process to generate the Authorize header, then the server performs a reverse of the procedure using the stored secret key to authenticate the request. Additionally, the server checks the Date header value against server time to check for replay attacks.
Below is the source for an AuthorizeAttribute subclass (for an MVC3 REST API). The code is easily adaptable to other frameworks, such as an OpenRasta PipelineContributor. The injected Session property in this instance is an NHibernate Session, and the ApiKey class is mapped to a database table. Successful authentication adds a custom IPrincipal to the HttpContext. Note that none of the x-aws headers are being used.
public class ApiAuthenticateAttribute : AuthorizeAttribute
{
private static System.Text.UTF8Encoding utf8 = new System.Text.UTF8Encoding();
[Inject]
public ISession Session { private get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
var request = filterContext.HttpContext.Request;
IPrincipal principal = null;
if (request.Headers["Authorization"] != null && request.Headers["Authorization"].StartsWith("AWS "))
{
// Amazon AWS authentication scheme.
var credential = filterContext.HttpContext.Request.Headers["Authorization"].Substring(4).Split(':');
var apiKey = Session.Query<ApiKey>().Where(k => k.AccessKey == credential[0]).FirstOrDefault();
if (apiKey != null && !apiKey.IsDisabled && credential.Count() > 1)
{
// check the date header is present & within 15 mins
DateTime clientDate;
if (request.Headers["Date"] != null
&& DateTime.TryParseExact(request.Headers["Date"], "R", DateTimeFormatInfo.CurrentInfo, DateTimeStyles.AdjustToUniversal, out clientDate)
&& Math.Abs((clientDate - DateTime.UtcNow).TotalMinutes) <= 15)
{
// build the signature & check for match
var stringToSign = String.Format("{0}\n{1}\n{2}\n{3}\n{4}",
request.HttpMethod,
request.Headers["Content-MD5"] ?? "",
request.Headers["Content-Type"] ?? "",
request.Headers["Date"] ?? "",
request.RawUrl);
var hmac = new HMACSHA1(utf8.GetBytes(apiKey.SecretKey));
var signature = Convert.ToBase64String(hmac.ComputeHash(utf8.GetBytes(stringToSign)));
if (signature == credential[1])
{
principal = apiKey.ToPrincipal();
}
}
}
}
if (principal == null)
{
filterContext.Result = new HttpUnauthorizedResult();
}
else
{
filterContext.HttpContext.User = principal;
}
}
}
Generating API Keys can be done like so:
public ApiKey()
{
// Generate random keys by using RNGCryptoServiceProvider & Base64 encoding the output
// Key lengths match AWS keys.
var rngProvider = RNGCryptoServiceProvider.Create();
var bytes = new byte[15];
rngProvider.GetBytes(bytes);
// Do some magic to ensure we have uppercase & digits only.
AccessKey = Convert.ToBase64String(bytes).ToUpper().Replace("+", "0").Replace("/", "9");
bytes = new byte[30];
rngProvider.GetBytes(bytes);
SecretKey = Convert.ToBase64String(bytes);
}
For the client, most languages have freely available Amazon client code that can be easily adapted. Reusing a popular scheme like this saves a lot of time & energy over rolling a completely custom solution, particularly where a number of disparate client platforms are likely to be used.
Deploying to Azure from psake – “No snap-ins have been registered”
21/06/2011 § Leave a Comment
Cloud hosting really lends itself to continuous deployment approaches – scripted provisioning models and blue/green deployment are well catered for. In the case of Microsoft Azure, the Powershell Cmdlets integrate easily with our psake build scripts.
Unfortunately, the cmdlets are 64 bit only and most of the popular CI servers are java-based and recommend using the 32 bit JVM for a variety of presumably good reasons. This results in 32 bit powershell kicking off and generating the error message “No snap-ins have been registered for Windows PowerShell version 2”.
Following is a modification to the psake.cmd file that ensures the 64 bit version of powershell is called on 64 bit systems:
@echo off IF EXIST %windir%\sysnative\WindowsPowershell ( %windir%\sysnative\WindowsPowerShell\v1.0\powershell.exe -NoProfile -ExecutionPolicy unrestricted -Command "& '%~dp0\psake.ps1' %*" ) ELSE ( powershell -NoProfile -ExecutionPolicy unrestricted -Command "& '%~dp0\psake.ps1' %*" )
DotLessResult – MVC ActionResult for dotless
23/05/2011 § 2 Comments
I’ve recently had the need to generate dynamic css for a themable MVC web application. I considered using a basic homegrown templating mechanism, or repurposing Razor, but then found the dotless project on the interwebs.
With the NuGet package it was relatively easy to get up & running, and I’ve abstracted the clunky regex code into an ActionResult subclass, recorded here in case it’s useful for somebody else.
public class DotLessResult : ActionResult
{
public IDictionary<string, string> Parameters { get; set;}
public string Less { get; set; }
public bool Minify { get; set; }
public DotLessResult(string less, IDictionary<string, string> parameters = null, bool minify = false)
{
Less = less;
Parameters = parameters ?? new Dictionary<string, string>();
Minify = minify;
}
public DotLessResult(Stream stream, IDictionary<string, string> parameters = null, bool minify = false)
: this(new StreamReader(stream).ReadToEnd(), parameters, minify) { }
public override void ExecuteResult(ControllerContext context)
{
var output = Less;
foreach (var key in Parameters.Keys)
{
output = Regex.Replace(output, @"\s*@" + key + @":\s*\S+;", "@" + key + ":" + Parameters[key] + ";");
}
var css = dotless.Core.Less.Parse(output, new DotlessConfiguration { MinifyOutput = Minify });
context.HttpContext.Response.ContentType = "text/css";
using (var writer = new StreamWriter(context.HttpContext.Response.OutputStream, Encoding.UTF8)) {
writer.Write(css);
writer.Flush();
}
}
}
This can then be used from your action method like so:
public ActionResult Styles(string id)
{
var stream = GetType().Assembly.GetManifestResourceStream(stylePath + id.Replace(".css", ".less"));
if (stream == null)
{
return HttpNotFound();
}
Dictionary<string, string> parameters = new Dictionary<string, string>();
parameters["backgroundcolor"] = "#1f1400"; // continue for all replaceable parameters
return new DotLessResult(stream, parameters, true);
}
The .less source should be stored in your web assembly as an embedded resource, with configurable parameters declared using the syntax “@: <placeholderValue;". Then it's just a case of looping through the supplied parameters dictionary and changing the declaration in the .less source. In the example code here I’m looking for a .less resource with the same name as the requested .css file, but you can obviously use your imagination.