azure, build, javascript comments edit

I’m in the process of creating some custom pipeline tasks for Azure DevOps build and release. I’ve hit some gotchas that hopefully I (and you!) can avoid. Some of these are undocumented, some are documented but maybe not so easy to find.

Your Task Must Be In a Folder

You can’t put your task.json in the root of the repo. Convention assumes that your task is in a folder of the same name as the task. Azure DevOps won’t be able to find your task in the extension if it’s in the root.


+- task.json
+- vss-extension.json

I messed with it for a really long time, you really just need that task folder. They show it that way in the tutorial but they never explain the significance.


+- YourTaskName/
|  +- task.json
+- vss-extension.json

You Can Have Multiple Task Versions in One Extension

You may have seen tasks show up like NuGetInstaller@0 and NuGetInstaller@1 in Azure DevOps. You can create a versioned task using a special folder structure where the name of the task is at the top and each version is below:

+- YourTaskName/
|  +- YourTaskNameV0/
|     +- task.json
|  +- YourTaskNameV1/
|     +- task.json
+- vss-extension.json

This repo has a nice example showing it in action.

Don’t Follow the Official Azure Pipeline Tasks Repo

The official repo with the Azure DevOps pipeline tasks is a great place to learn how to use the task SDK and write a good task… but don’t follow their pattern for your repo layout or build. Their tasks don’t get packaged as a VSIX or deployed as an extension the way your custom tasks will, so looking for hints on packaging will lead you down a pretty crazy path.

This repo is a bit of a better example, with some tasks that… help you build task extensions. It’s a richer sample of how to build and package a task extension.

You Have Two Versions to Change

In a JavaScript/TypeScript-based project, there are like four different files that have versions:

  • The root package.json
  • The VSIX manifest vss-extension.json
  • The task-level package.json
  • The task-level task.json

The only versions that matter in Azure DevOps are the VSIX manifest vss-extension.json and the task-level task.json. The Node package.json versions don’t matter.

In order to get a new version of your VSIX published, the vss-extension.json version must change. Even if the VSIX fails validation on the Marketplace, that version is considered “published” and you can’t retry without updating the version.

Azure DevOps appears to cache versions of your tasks, too, so if you publish a VSIX and forget to update your task.json version, your build may not get the latest version of the task. You need to increment that task.json version for your new task to be used.

You’re Limited to 50MB

Your VSIX package must be less than 50MB in size or the Visual Studio Marketplace will reject it.

This is important to know, because it combines with…

Each Task Gets Its Own node_modules

Visual Studio Marketplace extensions pre-date the loader mechanism so your extension, as a package, isn’t actually used as an atomic entity. Let’s say you have two tasks in an extension:

+- FirstTask/
|  +- node_modules/
|  +- package.json
|  +- task.json
+- SecondTask/
|  +- node_modules/
|  +- package.json
|  +- task.json
+- node_modules/
+- package.json
+- vss-extension.json

Your build/package process might have some devDependencies like TypeScript in the root package.json and each task might have its own set of dependencies in its child package.json.

At some point, you might see that both tasks need mostly the same stuff and think to move the dependency up to the top level - Node module resolution will still find it, TypeScript will still compile things fine, all is well on your local box.

Don’t do that. Keep all the dependencies for a task at the task level. It’s OK to share devDependencies, don’t share dependencies.

The reason is that, even if on your dev box and build machine the Node module resolutiono works, when the Azure DevOps agent runs the task, the “root” is wherever that task.json file is. No modules will be found above there.

What that means is you’re going to have a lot of duplication across tasks (and task versions). At a minimum, you know every task requires the Azure Pipeline Task SDK, and a pretty barebones “Hello World” level task packs in at around 1MB in the VSIX if you’re not being careful.

This means you can pretty easily hit that 50MB limit if you have a few tasks in a single extension.

It also means if you have some shared code, like a “common” library, it can get a little tricky. You can’t really resolve stuff outside that task folder.

You might think “I can Webpack it, right?” Nope.

You Can’t Webpack Build Tasks

I found this one out the hard way. It may have been in the past this was possible, but as of right now the localization functions in the Azure Pipeline Task SDK are hard-tied to the filesystem. If the SDK needs to display an error message, it goes to look up its localized message data which requires several .resjson files in the same folder as the SDK module. Failing that, it tries some fallback… and eventually you get a stack overflow. The Azure Pipelines Tool SDK also has localized stuff, so even if you figured out how to move all the Task SDK localization files to the right spot, you’d also have to merge in the localization files from other libraries.

The best you can do is make use of npm install --production and/or npm prune --production to reduce the node_modules folder as much as possible. You could also selectively delete files, like you could remove all the *.ts files from the node_modules folder. A few of these tricks can save a lot of space.

It’s Best to Have One Task Per Repo Per Extension

All of the size restrictions, complexity around trimming node_modules, and so on means that it’s really going to make your life easier if you stick to one task per extension. Further, it’ll be even simpler if you keep that in its own repo. It could be a versioned task, but one VSIX, one task [with all its versions], one repo.

  • You won’t exceed the 50MB size limit.
  • You can centralize/automate the versioning - the VSIX vss-extension.json and the task task.json versions can stay in sync to make it easier to track and manage.
  • Your build will generally be less complex. You don’t have to recurse into different tasks/versions to build things, repo size will be smaller, fewer moving pieces.
  • VS Code integration is easier. Having a bunch of different tasks with their own tests and potentially different versions of Mocha (or whatever) all over… it makes debugging and running tests harder.
  • Shared code will have to be published as a Node module, possibly on an internal feed, so it can be shipped along with the extension/task.
  • GitHub Actions require one task per repo.

Wait, what? GitHub Actions? We’re talking about Azure DevOps Pipelines.

With Microsoft’s acquisition of GitHub, more and more integration is happening between GitHub and Azure DevOps. You can trigger Azure Pipelines from GitHub Actions and a lot of work is going into GitHub Actions. Some of the Microsoft tasks are looking at ways to share logic between both pipeline types - write once, use both places. Having one task per repo will make it easier to support this sort of functionality without having to refactor or pull your extension apart.

autofac, net comments edit

Today the Autofac team and I are pleased to announce the release of Autofac 5.0!

This is the first major-version release we’ve had in about three years (Autofac 4.0 was released in August 2016). There are some breaking changes and new features you should know about as you decide your upgrade strategy. Hopefully this will help you navigate those waters.

Breaking Changes

Framework Version Targeting Changes

Starting with Autofac 5.0 there is no longer support for .NET 4.5.x. .NET 4.5.2, the last release in that line, follows the same support lifecycle as Windows Server 2012 R2 which ended mainstream support in September 2018.

Autofac 5.0 now targets:

  • netstandard2.0
  • netstandard2.1
  • net461

Containers are Immutable

The container registry can no longer be updated after it has been built.

The ContainerBuilder.Update method was marked obsolete in November 2016 and there has been a robust discussion to answer questions about how to get the container contents to adjust as needed at runtime.

ContainerBuilder.Update has now been removed entirely.

If you need to change registration behavior at runtime, there are several options available to you including the use of lambdas or child lifetime scopes. See this discussion issue for examples and ideas. We will work to add some documentation based on this issue.

[PR #948 - thanks @weelink!]

Lifetime Scope Disposal Hierarchy Enforced

Resolving a service from a lifetime scope will now check all parent scopes to make sure none of them have been disposed.

If you dispose a lifetime scope, all children of that lifetime scope will stop resolving objects. In cases like this you’ll start getting ObjectDisposedException instead.

If you have a custom application integration that involves creating/destroying lifetime scopes (e.g., custom per-request support) this may cause issues where proper disposal ordering is not occurring.

[Fixes #1020; PR #1061 - thanks @alistairjevans!]

Prevent Auto-Injecting onto Static Properties

Autofac will no longer do property injection on static properties when auto-wiring of properties is enabled.

If your application behavior depends on static property injection you will need to do some additional work like adding a build callback to populate the property.

[Fixes #1013; PR #1021 - thanks @alistairjevans!]

Features and Fixes

Asynchronous Disposal Support

Autofac lifetime scopes now implement the IAsyncDisposable interface so they can be disposed asynchyronously.

await using (var scope = container.BeginLifetimeScope())
   var service = scope.Resolve<ServiceThatImplementsIAsyncDisposable>();
   // When the scope disposes, any services that implement IAsyncDisposable will be
   // Disposed of using DisposeAsync rather than Dispose.

[PR #1037 - thanks @alistairjevans!]

Nullable Reference Type Annotations

Autofac is now build using nullable reference type annotations. This allows developers to get sensible compiler warnings if they opt-in, thus avoiding NullReferenceException instances where possible.

Nullable reference type warnings

[PR #1037 - thanks @alistairjevans!]

Build Callbacks in Lifetime Scopes

One method of running code at container build time is by registering a build callback. Previously this only worked at the container level, but we’ve added the ability to register callbacks that run at lifetime scope creation as well.

var scope = container.BeginLifetimeScope(cfg =>
    cfg.RegisterBuildCallback(scope => { /* do something */ });

The callback will be invoked just prior to BeginLifetimeScope exiting, after any startable components are instantiated.

[Fixes #985; PR #1054 - thanks @alistairjevans!]

Other Fixes

Still TODO

Now that Autofac 5.0 is out, there is still a lot to do. We’ll be working on these things as fast as we can:

  • Updating integration packages we support so they ensure compatibility with Autofac 5.
  • Updating the documentation to reflect the above changes.

Some of this is sitting in branches ready to go, other things need to be done now that we have this core package out there.

If your favorite integration isn’t ready yet, we’re doing our best. Rather than filing “When will this be ready?” issues, consider pull requests with the required updates.

Thank You!

On a more personal note, I’d like to thank all the folks that threw code at Autofac in the past few months. We appreciate the effort and the contributions. NuGet tells me we’re at 41,587,203 total downloads as I write this, #27 on the list of top package downloads in the last six weeks. We have 25 integration packages we maintain along with documentation, examples, and support.

Without your contributions this wouldn’t be possible. Thank you so much!

Hopefully git shortlog and the GitHub contributors page didn’t fail me - I don’t want to miss anyone!

personal comments edit

I’ve been having trouble of late when trying to repair robots at home. This is leading me to the belief that if anything you own has a battery, that battery must be user-serviceable. I’m going to start looking for things with this specific feature.

Robot repair is, admittedly, not the most common occurrence, but within the last few months there have been some robotic mishaps.

The first issue came along with my Sphero. I had one of the originals and it was working pretty well, but I left it unused for a few months while I was doing other things. When I came back it wouldn’t hold a battery charge anymore.

The body on a Sphero is a sealed plastic ball. You can’t open it or do anything with it. Once it dies, you throw it out. Which is a shame, because with a new battery it’d be good as new.

Luckily, despite the age of the robot, the super amazing awesome folks at Sphero replaced it at no cost. I got a newer robot which paired easier with my phone (the old one was giving me problems, likely due to an old firmware) and it’s sweet.

Of course… I figured I’d try to replace the battery in the old one anyway. Why not? Worst case scenario I’d be in the same position I was already in - one working robot, one non-working robot.

I took a Dremel and opened the sphere. I bought some 70mm clear plastic ornaments to serve as a replacement body. I looked at the battery in there and bought a replacement of the same type. Easy enough to unscrew the body part that covers the battery, unplug the old one, plug in the new one, and put it all back together.

I did all that and stuck it on the charger for a day. It appeared to charge, but… it just wouldn’t wake up. Hmmm. I unplugged the battery, plugged it back in, put it back on the charger for a day. Next day I tried again and it woke up… but wouldn’t pair with my phone. Or the iPad. Or anything else. It just blinked until the pairing timed out.

I have no idea what went wrong there. I’m guessing there was something else messed up, or the new battery wasn’t as identical as I thought it was… or something.

That robot went to the trash and now I have a bag of clear ornaments and a battery pack that doesn’t go to anything.

The second issue was with a “BB-8 Hero Droid”. This is a gift I got my wife a few years back. It follows you around, responds to voice commands… pretty fun.

Just like with my Sphero, BB-8 went unused for a few months. After we tried charging him up, he wouldn’t hold a charge.

I found this video where a guy takes one of these apart. Seemed involved but easy enough.

Today I got around to doing that, being on vacation for the holidays. I was hoping to get it fixed up for my wife for Christmas.

With no small effort I got the shell of the BB-8 body off. All the parts were labeled in the order in which I removed them, I had a bunch of photos, and the video also shows the exact steps I did.

Once the body is off, though… that’s where the video ends. There aren’t instructions for actually replacing the battery. It’s buried right in the center of the thing, too. In for a penny, in for a pound. Let’s do this.

I got two or three screws off the motor and realized I needed to detach the power switch on the side because it was holding the two halves together. I unscrewed one part, then started unscrewing the other…

…and I guess my screwdriver was in the power charging port or something rather than on an actual screw because fire shot out of the side of the motor and smoke started rolling out. FUUUUUUUUUUUUUUUUUUUUUUUUUUUU

I just about ran for the fire extinguisher but yanking all the cables out of the main board stopped the power flow and the fire. It did not, however, leave things in a functional state.

I feel really bad. I didn’t mean to kill Jenn’s BB-8 but… I guess it’s not a worse situation than it was before - the robot didn’t work before, now it still doesn’t work. But I still feel bad.

I had a small funeral for the BB-8 as I put his parts in the trash can. He is survived by the R-unit that Jenn made when we were in Disney World last month. I guess next trip we make to a Disney theme park she’ll have to make a new BB-unit to replace ol’ Hero Droid.

Anyway, if it has batteries, I need to be able to replace them without breaking into the device, lighting anything on fire, or otherwise causing the device to stop functioning. That should be a thing. I would also accept “lifetime warranty on the battery” such that I can take a unit that isn’t functioning due to the battery and have the factory replace either the battery or the whole unit.

(Yes, I realize there’s some interesting coincidence/irony about this post follwing the post about grounding the yellow wire to hack a fan into working but none of my fans have started a fire. Yet.)

hardware comments edit

WARNING I’m going to explain a total hack here that worked for me. If you do this, any consequences are totally on you. I’m not responsible if your hardware gets fried.

I have a Synology Diskstation DS1010+ with… loud fans. It’s old, the fans are starting to sound like a rock tumbler. For the life of me, I can’t find a quiet fan alternative. I can replace the fans with the same ones I already have, but they’ve always been kind of loud.

Unfortunately, the fans in the DS1010+ are the kind that detect locked rotors.

Let me explain.

Fans for computers come in three-wire or four-wire varieties. Let’s focus on three-wire because that’s what the DS1010+ and many other systems use.

The three wires are sometimes:

  • Red: Power
  • Black: Ground
  • Yellow: Fan speed sensor

However, server fans usually don’t control the speed this way; instead they want to detect if the fan is dead. So the three wires there are:

  • Red: Power
  • Black: Ground
  • Yellow: Locked rotor detection

What’s important to know about the locked rotor signal is that if the signal is grounded then the server thinks the fan is still spinning. Alarms don’t go off, things work as normal. When the rotors get locked, power comes out that yellow cable and that’s when the alarms sound.

I bought really nice be quiet! cooling fans that are totally quiet… but they have a speed sensor on the yellow wire, not locked rotor detection.

This caused the Diskstation to generate all sorts of fan alarms - flashing lights, emails, beeping, the whole thing. You can search for this in forums, lots of people see it. You can silence the beeping and stop the emails, but you’ll still periodically get a yellow flashing light on the side, the fans spin up like jet engines, and alerts show up in the UI. No, thanks.

I tried building this circuit that someone figured out that would fake the rotor lock. Either I did it wrong or I have yet different fans than that person has. Point being, that was some time I’ll never get back.

The missing piece is that info about grounding. I found it on that page:

As long as it is rotating, the signal pin is tied to ground, but left floating once the rotor stopped spinning (the output stage of these types of fans is of an open collector type and pulled up by the main board. Hence, “floating” is equivalent to “high”).

I don’t really care if the Diskstation, at this point in its way-past-the-warranty life, alerts me about the fans. They’re gonna work. It’ll be fine. I just need the beeping to stop.

The answer: clip the yellow wire between the fan and the main board. On the side that comes from the fan, just bundle it up so it doesn’t make contact with anything. On the side that connects to the main board, connect the other end of the yellow wire to the case somewhere that’s grounded.

WARNING I’ll again warn you, this works for me. If it doesn’t work for you and things overheat and your house burns down or you lose all your data, that’s not my fault. Do this at your own risk!

Here’s a picture:

Ground the yellow wire!

I did this with both fans in the Diskstation and it’s been quiet for days.

I also did this with an HP ProCurve 2810-24G switch - the fans in there are like jet engines, no joke. You can’t be in the room with the thing. Swapped them out for some nice equivalent Noctua fans and it’s so quiet… but it also required that I ground the yellow wire or it would fill up error logs and lights would flash.

Now I have new quiet fans, no alarms, and things are working great.

FINAL WARNING Last time, if you do this, it’s all at your own risk!

personal comments edit

I live on the west coast of the US so while I’d been to Disneyland in California a few times, I hadn’t been to Disney World. We decided this year was the time to take that trip.

We stayed at Kidani Village, which is a resort in the Animal Kingdom portion of Disney World. It was pretty cool because outside the room (well, air-gapped, so your room, then a fence, then some space, then another fence) are animals like zebras, ostriches, and ankole cattle. Depending on the room, there may be giraffes and other animals.

The first thing that struck me about Disney World is the sheer scale. It’s 44 square miles all told - the resorts, the parks, everything. That means everything in general is big. It was a quarter mile between the lobby and our room. At the end of a tired day, that’s a full five minute walk.

I filmed the walk. Watch it and be amazed.

The first day was a travel day. It’s about five and a half hours to fly from one side of the US to the other. We basically landed, went to dinner, and crashed.

The second day was spent in Hollywood Studios. This is roughly equivalent to California Adventure by Disneyland. The majority of the day was spent in Black Spire Outpost, the Star Wars section of the park.

I could spend a whole day in Black Spire Outpost. The whole thing is so immersive, so well done. If you are a Star Wars fan at all, this is the coolest thing ever. The cast members there are in character, stormtroopers randomly patrol the streets, sometimes Chewbacca just walks by. At night it’s lit up and is gorgeous. You can look around and you’d never know you were in a Disney park.

We pretty much bought everything they sold here. Take my credits! We built a lightsaber, we built a custom droid, we adopted a pet, we bought literally every drink they sold in the cantina. We had the blue milk with and without alcohol.

I would say the “can’t miss” items here are flying the Falcon and building the lightsaber.

The Falcon flight is sort of short but is super fun, something any Star Wars fan has wanted to do forever. There’s a photo op area inside where you can take your picture at the chess table.

Sitting in the Millennium Falcon

The ride is actually responsive to the controls, like a video game, not just a ride on rails. You have to work as a four-person team to capture some cargo. One pilot controls left/right, one controls up/down. There’s a gunner to take care of incoming bad guys and an engineer who is in charge of nabbing the cargo from a moving ship.

Building a lightsaber is… it’s like participating in a live action version of a scene from Star Wars. It takes place in “Savi’s Workshop,” where some “junk gatherers” guide you through the process of building a custom lightsaber.

Savi's Workshop

The cast members there give you a speech about the various Jedi who carried the different saber colors and what each color conveys. The whole speech is timed to this background music that plays like a soundtrack and changes with the mood. As they talk about each color, the lights in the room change to reflect it. Voices of the Jedi speak like ghosts from beyond.

The build itself is fairly straightforward but you probably won’t be prepared for how heavy the saber hilt is. It’s like two pounds. It’s solid metal around a plastic holder that handles the electronics.

The kyber crystal you put in the saber is also interesting. It controls both the color of the blade as well as the sounds the saber makes when it moves, turns on, and turns off. You can get different crystals at the shop (naturally) and if you swap them into your lightsaber it will change the colors and sounds - it’s not that you need a different blade or anything.

Further, you can get these little boxes called “holocrons” where you can put a crystal into the box and it’ll glow the color of the crystal and share “stored knowledge” (recorded lines from various movie characters) associated with the crystal.

Anyway, do the Millennium Falcon flight and make a lightsaber. It’s sweet.

Jenn pried me away from Star Wars land for a little at the end of that day and we rode some of the Pixar rides. Slinky Dog Dash is a pretty good little coaster.

That night we went to the “Jingle Bell Jingle BAM!” dessert party, which was basically an all-you-can-eat dessert bar with some interesting holiday treats, a meet-and-greet with Chip and Dale, and front row seats for a really cool music and light show. It was super fun and even after a whole day of walking, Phoenix was dancing through the show.

The second day was spent at Animal Kingdom. I liked this a lot more than I thought I would. There’s a safari style ride where they drive you around through a preserve and you can see the animals there. It reminds me a lot of Wildlife Safari in southern Oregon. Really fun, really interesting.

The “Expedition Everest” ride here is pretty fun, a decent roller coaster. I’d recommend hitting this one if you get there.

We didn’t get to see a lot of the shows, but we did find Kevin from Up! running around the park and dancing to bands playing in various areas.

Day three was Epcot. Hmmm.

If you go, unless you’re a real foodie, try to avoid the Food and Wine Festival. This brings in a lot of folks who really want to “drink around the world” and wear reasonably annoying - if not obnoxious - group t-shirts talking about how drunk they’re going to get. If you go during the Food and Wine Festival, try to go on a day with crappy weather rather than nice, clear, sunny weather. If you have to go during the Food and Wine Festival and you end up with beautiful weather, by all you deem holy try not to go on a holiday weekend.

We hit the trifecta. It was so packed. Like SO SO SO PACKED. People were just there getting trashed and we’re not the only ones who noticed. I heard rumor that there was a fight amongst the drunks.

It doesn’t help that most of the “foodie food” isn’t palatable to the eight-year-old set, so finding “regular kid food” which consists of chicken tenders, cheese pizza, or some similar bland-yet-kid-standard fare is a lot of walking.

We spent the majority of the time either looking for kid-friendly food [which didn’t require standing in an hour-or-more line] or hanging in the other area of the park where “Mission Space” and “Spaceship Earth” are. We did enjoy these rides but I’d like to go back and see the countries at Epcot when there aren’t a bunch of people trashing it up.

Days four and five were Magic Kingdom. This is basically the same as original Disneyland. Most of the rides are the same. There are a couple of rides Disneyland doesn’t have, like the Peoplemover and the Carousel of Progress. Some rides are slightly different, too, like “It’s a Small World” and the Haunted Mansion are both just a little different.

The one thing I’ll focus on is the tiki room. I love the Enchanted Tiki Room. We go to Disneyland, I look forward to grabbing a Dole Whip and heading into a kitschy show of 50’s audio-animatronic birds. It makes my heart smile. They changed the show in Disney World so it doesn’t have the fountain in the middle and the birds seem different, not as retro or cartoon looking. It wasn’t the same and I was disappointed.

We did attend the Very Merry Holiday Party and that was really cool. The Magic Kingdom park closes to the public at 6:00 PM for these and guests with party tickets can stay until midnight. They have stands handing out cookies, hot cocoa, and cider. Rides change to holiday themes like “Jingle Cruise.” Space Mountain lights and music all switch to holiday music. It’s insane and awesome.

The last day was a half day at Hollywood Studios where we saw the rides we didn’t see the first time around. The Tower of Terror is my all-time favorite ride, so I’m glad we got to ride it again. (I’m sort of sad they changed the one in California to be Guardians of the Galaxy theme.) We also saw the Aerosmith “Rock n Roller Coaster” which was a great ride, too.

The remainder of that last day was travel back home. Another long flight.

In all, this was a great vacation. It was worth doing once, and I’d love to go back in the future to see more of Animal Kingdom and Epcot. In the meantime, I definitely need another Star Wars hit and original Disneyland in California will be much more affordable for me on my side of the country.

Also, it wouldn’t have happened if Jenn hadn’t planned the whole thing and watched altogether too many YouTube videos on where to eat, where to stay, how to take advantage of all the deals, and so on. She’s amazing and both Phoe and I owe her a debt for all the work she put into it.

Finally, if you want to see a walkthrough of how much Disney got us for, here’s an overview of all the stuff we got.