How to use git with Jenkins on Windows

The basic idea

You have to install git and get it into the path of the user account running Jenkins. We’ll take advantage of Github for Windows’ installation of git to do this.

How I did it

  1. Ensure that the Jenkins service is running under an account that you can actually log into. We’ll refer to this as JenkinsServiceAccount from here on.
  2. Login to the box with the JenkinsServiceAccount.
  3. Install Github for Windows. When it’s finished, just close the app if it comes up.
  4. There’ll be a shortcut to Git Shell on the desktop, right click that and click Open File Location.
  5. In the folder that opens, you’ll see a file named shell.ps1. Open it in your favorite text editor.
  6. Find the line where $env:github_git is set. Mine was set to:
    C:\Users\JenkinsServiceAccount\AppData\Local\GitHub\PortableGit_fed20eba68b3e238e49a47cdfed0a45783d93651
  7. You need to add three subfolders of that folder to the system PATH (via System Properties -> Environment Variables -> System Variables). What I added looks like this:
    C:\Users\JenkinsServiceAccount\AppData\Local\GitHub\PortableGit_fed20eba68b3e238e49a47cdfed0a45783d93651\cmd;C:\Users\JenkinsServiceAccount\AppData\Local\GitHub\PortableGit_fed20eba68b3e238e49a47cdfed0a45783d93651\bin;C:\Users\JenkinsServiceAccount\AppData\Local\GitHub\PortableGit_fed20eba68b3e238e49a47cdfed0a45783d93651\mingw\bin
  8. Restart the Jenkins service and it should now be able to use git.

Probable Gotcha

The path to PortableGit looks like it has a changeset in the folder name which means that if you update Github for Windows, that path might change. This is dumb, however, I don’t believe Github for Windows will update if you never open it. So, never open it.

Posted in Uncategorized | Tagged , , , , , | Comments Off on How to use git with Jenkins on Windows

Use SlowCheetah for Web.config transform previews

I’m only two years late to this game, but I finally needed to use web.config transforms on a project. Scott Hanselman has a helpful post on it.

Posted in Uncategorized | Comments Off on Use SlowCheetah for Web.config transform previews

Don’t put ng-app and ng-view on the same element.

I was having a problem where Angular’s routing system wasn’t working in my application. After some experimentation I found this out:

If you have an element like

<div ng-app="app" ng-view>Stuff</div>

then links which go to known routes won’t get handled by Angular’s, rather, they just act like a normal link would. The solution was simple: put each attribute on a different element.

<div ng-app="app">
    <div ng-view>
        Content
    </div>
</div>

Angular version: 1.2.0rc2

Posted in Development | Tagged , , , , , , | Comments Off on Don’t put ng-app and ng-view on the same element.

Name your Angular controller initialization functions so they appear in call stacks

So until yesterday I would create controllers like this:

angular.module('myApp', []).controller('DemoController', ['$scope', function($scope) {
    // awesome stuff here
}]);

However, in call stacks, the code executing inside the controller’s function just shows up as (anonymous method). If I actually name the function, then it shows up as that in the call stack. So now I write it this way:

angular.module('myApp', []).controller('DemoController', ['$scope', function DemoController($scope) {
    // awesome stuff here
}]);

This technique works for any anonymous function in JavaScript, not just for Angular app.

Posted in Uncategorized | Comments Off on Name your Angular controller initialization functions so they appear in call stacks

Navigate to URLs outside of the Angular application with target=”_self”

Today I brought in Angular’s routing to a view I was working on, clicked on a link which is on the same server but outside of the Angular application, and my browser’s URL updated, but the screen was just blank. Angular had hijacked the link and tried to route to the href itself.

I found the solution in the Angular Google Group.

<a href="/someUrl/inTheSameDomain" target="_self">
A link outside the Angular app
</a>

Adding target=”_self” to the link makes it so that Angular won’t use its own routing to try to navigate to that URL.

Posted in Uncategorized | 1 Comment

Use ng-src with img elements

I had an an img element that looked like this:

<img src="{{post.imageUrl}}" />

And I saw that the page was making a request for “/{{post.ImageUrl}}”.

To prevent templated img elements from requesting their templated markup, use ng-src like:

<img ng-src="{{post.imageUrl}}" />
Posted in Uncategorized | Tagged , , , | Comments Off on Use ng-src with img elements

Static resource caching and cache-busting with ASP.NET MVC and AppHarbor

Serving static content (javascript, css, images) with headers specifying long cache expirations is easy. However, cache-busting those is hard. In this post, I’ll show how to deliver static content with cache-busting URLs.

I’m going to show code which I wrote specifically to use with AppHarbor, but you may find that you can use the same techniques in different environments.

Cache-busting Technique: Versioned Path

The most reliable method of cache busting is to put some version identifier in the path to the static content such as:

http://localhost/statics/12345678/css/style.css

Where 12345678 is the version identifier. This method even works with CDNs which might otherwise ignore lesser important path tokens like querystring parameters or an identifier after a hash mark.

In the code below, I’ll show a solution which includes the version identifier in the path to the static content Version identifier changes whenever a build is released to AppHarbor.

How It Works

A browser makes a request to your server for your homepage. The server delivers the homepage and references to static resources using the versioned path.

The browser requests those static resources from your server. The server finds the resources requested, adds headers,

Cache-Control: public
Expires:Fri, 15 Feb 2013 02:45:01 GMT // a year from now

and serves them.

The browser keeps the resources in its cache for a long time and future requests for them are served from cache.

You ship new code to AppHarbor. AppHarbor builds and deploys your application. It also adds a configuration setting into your web.config with a key of appharbor.commit_id and the value is set to the last changeset ID. Your server uses that changeset ID as the  version identifier in static resource paths.

The browser makes a request for your homepage again. The server delivers the homepage, but this time references the static resources using the new version identifier.

The browser now must request those static resources again because the version identifier has changed. The server serves up the updated files back to the browser where they’re cached again.

Caveat: browsers will have to request all static resources again every time a build is deployed. I’m very happy to accept this. Alternatively, you could do crazy things like get a hash of the file and serve that as part of the file’s path. See Karl Seguin’s article about cache busting if you’re interested in that sort of thing.

How To Do This In Code

First, you’ll need to be able to provide static resource urls. I like to write them like this:

Which means I’ll need an extension method on the UrlHelper:

In the StaticContent method, I’m populating the staticResourceVersion variable from a configuration value which is in my web.config. If one is not set, like when you’re running your site locally, “_default” is provided.

Another thing to note is that if the staticServer variable is populated, then it’ll be prepended to the returned url. This supports serving static content from a cookie-less domain.

Now that static content can be referenced, we need to have a route and a controller to handle requests for static content.

The first and third routes are the default ones that come with MVC. The middle route understands how to handle requests which start with s/ followed by the version identifier and then whatever the path to the resource is. It directs those requests to the StaticsController’s Index action.

The StaticController’s Index action first needs to check if the requested path is allowed. We certainly wouldn’t want to serve just any content back through here. (I’m showing a really simple IsAllowed implementation, please feel free to be smarter about yours.)

For my own sanity, when I’m deving on my localbox, I don’t ever want content cached. So the caching headers are only set when the request is not local.

And finally, the file is served up with the correct content type. (Again, simple implementation, feel free to be smarter.)

The staticResourceVersion variable is ignored; I haven’t thought of anything to do with it yet.

And the hard work is done! Update your static resource urls to use the Url.StaticContent helper, deploy out to AppHarbor, and bask in the the glory of long-cache-expiration-headered static content.

The End

Special thanks to my fellow devs at Cheezburger for teaching me this technique.

Thanks to Michael Friis for telling me that AppHarbor inserts the commit_id into a deployed web.config.

Posted in Development | Tagged , , , , , | 4 Comments

jQuery UI Sortable + Bootstrap’s Buttons = Perfect Performance Storm

I ran into this crazy performance problem on:

  • jQuery 1.7.1
  • jQuery UI 1.8.16
  • Twitter’s Bootstrap 1.4.0
  • Windows 7 / Chrome 16.0.912.63

See how the button starts dragging from the top left corner and drags really slowly? I stripped down the CSS element by element using Chrome’s dev tools and was able to isolate the problem to the -webkit-transition and box-shadow styles on the .btn class. Setting those to none fixed the problem.

Posted in Development | Tagged , , , , , | 7 Comments

Using jQuery to provide an inline confirmation on buttons

I was working on a side project and wanted the user to confirm when they wanted to delete something. The normal window.confirm method was just too primitive so I decided to write something I liked a little more.

Click the delete button below to see it in action. Click the JavaScript tab below to see how it works.

This was built for use with the amazing Bootstrap CSS framework. Only the classes that are added/removed are dependent on Bootstrap, so adjust to your needs.

Posted in Development | Tagged , , , , | 4 Comments

Getting the version number of your own Chrome Extension

This is so kludgy. I hope they make it better sometime, though :-/

 

Posted in Development | Tagged , , , , | Comments Off on Getting the version number of your own Chrome Extension