C#: Parsing a CSPROJ (Project) File Using XPath

Using XPath in C# can be done several different ways through a several built-in libraries, and none of them work unless you are a lot more familiar with the file than would be required in many other languages. However, to make matters worse, you might be further required to do some unintuitive shenanigans. In the way of an example, this is how to retrieve the assembly-name:

XNamespace xmlns = "http://schemas.microsoft.com/developer/msbuild/2003";
XDocument projDefinition = XDocument.Load(projectFilepath);

IEnumerable<XNode> assemblyResultsEnumerable = projDefinition
	.Element(xmlns + "Project")
	.Elements(xmlns + "PropertyGroup")
	.Elements(xmlns + "AssemblyName").Nodes<XContainer>();

IList<XNode> assemblyResults = new List<XNode>(assemblyResultsEnumerable);
if(assemblyResults.Count == 0)
{
	throw new Exception(String.Format("The project file isn't correctly structured: [{0}]", projectFilepath));
}

string assemblyName = assemblyResults[0].ToString();

Notice that we have to mash the namespace URL with the node-name in order to find the node.

Go: Encoding Maps with Non-String Keys to JSON

I ran into some issues with 1.6.2 encoding a map to a JSON structure because it had int/int64 keys. Sure, JSON prescribes string-only keys, but I incorrectly made the reasonable assumption that Go would coerce these to strings. It turns out that this change was made in the very recent past and that, as of the last release (1.7.1) (and probably earlier ones) this should no longer be a problem.

Unfortunately, AppEngine still runs on 1.6.2, for now. So, if you had the same problem, you will have to go the conventional route and translate these keys to strings, yourself, prior to marshaling.

TFS Tasks Do Not Appear When Uploading a VSIX

I ran into a weird issue. I was experimenting with TFS tasks and constructing VSIX files because I have been away for a little while. I started with an existing, open-source project (which had a vss-extension.json file) and slowly morphed it from what was there to what I want it. However, I found that, when it came time to change the name of the “publisher” field in the VSIX manifest and the “author” field in the task manifest, I would do an upload and, although the VSIX uploaded and installed perfectly fine, the tasks would not show up.

Things I tried:

  • Using the publisher name off another project located in the Marketplace.
  • Using *my* personal publisher name (I am registered in the Marketplace).
  • Using a random string definitely not registered as an existing publisher.
  • Using a second, different project and trying the same changes to the publisher name, just in case there was something magical about the publisher in the first one (Microsoft, incidentally). There were some implied correlations between the publisher names, project names, scopes, and/or targets, and, most of those started with “ms” and I didn’t know whether this was significant and the real issue.
  • Since I was experiementing on an on-premise TFS 2015 instance colocated on my laptop, I cut my Internet connection and installed the VSIX of a brand-new project in order to determine whether it could run or if it potentially did publisher lookups against the Marketplace. The latter was not the case. It installed fine.

It turns out that, if you are going to change the publisher, you need to change the version as well. When you uninstall a VSIX, the TFS still remains polluted with prior knowledge of that VSIX and/or those tasks.

 

Using the Google Maps Client Library for Go in AppEngine

The default HTTP transport implementation for Go isn’t supported when running in AppEngine. Trying to use it will result in the following error:

http.DefaultTransport and http.DefaultClient are not available in App Engine. See https://cloud.google.com/appengine/docs/go/urlfetch/

To fix this, you need to use the http.Client implementation from AppEngine’s urlfetch package (imported from google.golang.org/appengine/urlfetch).

uc := urlfetch.Client(ctx)

options := []maps.ClientOption {
    maps.WithHTTPClient(uc),
    maps.WithAPIKey(GoogleApiKey),
}

c, err := maps.NewClient(options...)
if err != nil {
    panic(err)
}

nsr := &maps.NearbySearchRequest{
    Location: &maps.LatLng {
        Lat: latitude,
        Lng: longitude,
    },
    Radius: radius,
    OpenNow: true,
    RankBy: maps.RankByProminence,
    Type: maps.PlaceTypeRestaurant,
}

psr, err := c.NearbySearch(ctx, nsr)
if err != nil {
    panic(err)
}

Setting Environment Variables From PowerShell Tasks Under TFS 2015

This is less than intuitive, but you can hotwire some underlying functionality to set your own environment variables that will persist from task to task. Here, we take a value from the environment and set another value derived from it.

Write-Host "##vso[task.setvariable variable=ACTIONSFOLDER;]$env:BUILD_DROPFOLDER\Scripts\Steps"

Sorting All Files in Tree by Modified-Time (in OS X)

This command had to be modified to run under OS X as it, and its heritage, don’t support the “-printf” parameter:

$ find . -print0 | xargs -0 stat -f '%m %N' | sort -k 1,1 -n

Tail of output in my situation:

1469286497 ./dev_import/data/160609_103423/.160609_103423.processing/style-color
1469286497 ./dev_import/data/160609_103423/.160609_103423.processing/upc
1469286541 ./bip/config/log.py
1469286566 ./dev/run_dev.sh
1469286597 ./dev_import/data/160609_103423/events.log.20160723-110946
1469286624 ./dev_import/data/160609_103423/.160609_103423.processing
1469286626 ./dev_import/data/160609_103423
1469286626 ./dev_import/data/160609_103423/events.log.20160723-111026

Searching for Specific Packages in NuGet

Package searches in NuGet hit the query API and the query API, by default, does a substring search. This obviously might get annoying when a simple string that you’re searching for might appear within any of the searchable characteristics of many different packages.

However, since this is the standard query interface, and you can pass more than just flat strings, you can also modify the query to do an exact search using the “PackageId” modifier:

C:\&gt;nuget list zebus
Zebus 1.4.6
Zebus.Directory 1.2.10
Zebus.Directory.Cassandra 1.2.10
Zebus.Directory.Standalone 1.2.10
Zebus.Persistence 1.0.2
Zebus.Persistence.CQL 1.0.2
Zebus.Persistence.CQL.Testing 1.0.2
Zebus.Testing 1.4.6

C:\&gt;nuget list PackageId:zebus
Zebus 1.4.6

“System” process bound to port 80 in Windows: “Can not obtain ownership information”

I have a port forwarded from another host to my local port 80. Of course this is occasionally problematic as I have other things that also want to use port 80. Unfortunately, I have some projects that embed some URLs to point to port 80. So, I simply stop and start the various port 80 services as required. Unfortunately, sometimes everything is fine and other times I end-up with port 80 being bound by an unidentifiable running process seemingly no matter how often I restart, even when I’ve made sure that there are no port-80 sites in my IIS. I inexplicably don’t often run into this problem (it seems like I should).

netstat indicates that a process with PID 4 is bound to it but can’t give me its name (this is an elevated prompt):

1_Port80Bound

TCPView says that it’s actual “System” (read: Windows):

2_Port80Bound

However, it turns out that it’s the “World Wide Web Publishing Service”. Turn it off (or disable it, sue Microsoft for damages, etc..):

3_Port80Bound

 

You’ll be all set. Why does this service want port 80 when none of my sites are configured to use port 80? We’ll file it under “$!?!!%” for now.