Npm link, wait, what!? Yeah npm link, a way to test npm packages as you develop them. This is a little something I learned last week and need to write it down so I remember how to do it.

I was working on the THAT Conference api and needed to add a custom GraphQL scalar for a ‘Slug’. In this particular case I am working with member slugs, though the Slug scalar will be utilized for any of our slug needs.

The package project, that-api, will contain the new Slug scalar located locally at ~/tc/that-api.

The api project, that-api-members, will be using the new scalar located locally at ~/tc/that-api-members.

After a bunch of hacking, etc. its was time to test the npm package in the api project. Here are the steps. This assumes that both packages pass tests, build and run. Both these projects use Babel and output to a folder named __build__. Package name is @thatconference/api

Starting with that-api

$ cd ~/tc/that-api
$ npm run validate
$ npm run build
$ cd __build__
$ npm link
$ cd ..
$ npm run dev

Now at the api project that-api-members

$ cd ~/tc/that-api-members
$ npm run validate
$ npm link @thatconference/api
$ npm run validate
$ npm run start:watch

At this point the that-api-members project is running using the linked that-api package, not the one downloaded into its node_modules folder.

So to undo this. I am not 100% sure this is correct, but it seems to work.

Unlink the the package from that-api-members.

$ ^c
$ cd ~/tc/that-api-members
$ npm unlink @thatconference/api
$ npm i @thatconference/api

Stop the running code and unlink to remove the link from npm which removes it from global location `{prefix}/lib/node_modules/<package>`

$ ^c
$ cd ~/tc/that-api/
$ npm unlink
$ cd ..

When you unlink a package it removes it from packages.json, so we need to then add it back. This is why we removed the global link first so we could install and get the package, not the link.

A Few Notes

  • If you use a node version manager like nodenv, both the application and package need to run the same version or the application will not be able to locate the link.
  • Using the command npm --link=true may show linked location in your node_modules.

oct 2020: instruction fixes

It seems after every MacOS update these days I get this reoccurring issue about not being able to build GYP in a node project. I got tired of looking up the references to fix it and am consolidating them here for future reference.
Note: October 2020 update at the bottom (x-code 12).

The Error

The error that is thrown (or close to it is):

gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 1
gyp ERR! stack     at ChildProcess.onExit (/usr/local/Cellar/[email protected]/10.18.1/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:191:23)
gyp ERR! stack     at ChildProcess.emit (events.js:198:13)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:248:12)
gyp ERR! System Darwin 19.3.0
gyp ER

The Fix

XCode CLTools is require for this to work. I don’t know the details, only what has corrected this for me in the past.

Check if installed:
/usr/sbin/pkgutil --packages | grep CLTools

Nothing will be returned if they are not present. If they are, ¯\_(ツ)_/¯, more research I guess.

Install XCode cli only (may need to use sudo:
xcode-select --install

If you received the error, “xcode-select: error: command line tools are already installed, use “Software Update” to install updates” you will need to remove the xtools first. Per the documentation you do that by:
sudo rm -rf /Library/Developer/CommandLineTools

Run the install again, and all should install as expected (may need to use sudo).
xcode-select --install

Once the install is complete recheck if CLTools are present:
/usr/sbin/pkgutil --packages | grep CLTools

You should received a response something like:

That’s it, try your build again.

October 2020 Update Note:

I still run into this issue after most updates. It happened again after the macOS 10.15.7 update. Though this time when running the install (sudo xcode-select --install) a message popped up that the software could not be found on the server. After some Duck Duck Googling I found the X-Code CLI Tools download. After installing this I was back in action and able to build again.


The 12 factor app, guidelines to build applications which are scalable, flexible and simply great.

I. Codebase

One codebase tracked in revision control, many deploys

II. Dependencies

Explicitly declare and isolate dependencies

III. Config

Store config in the environment

IV. Backing services

Treat backing services as attached resources

V. Build, release, run

Strictly separate build and run stages

VI. Processes

Execute the app as one or more stateless processes

VII. Port binding

Export services via port binding

VIII. Concurrency

Scale out via the process model

IX. Disposability

Maximize robustness with fast startup and graceful shutdown

X. Dev/prod parity

Keep development, staging, and production as similar as possible

XI. Logs

Treat logs as event streams

XII. Admin processes

Run admin/management tasks as one-off processes

I started teaching myself Docker during my time off December 2016.  I grabbed my dockerUbuntu laptop, loaded up Docker and got to work.  Docker is really easy to pick up and get started with.  I even loaded it up and build a few containers for a Raspberry Pi.  🙂

With Windows 10 and Windows Server 2016, Windows containers are now available.  You can get a base Windows container from Docker hub and run it from one of these OS’s.  Be forewarned, the images made from Windows Core are 10GB in size!  So much for being light. At least you only need to download them once.

I have and old (unfinished) ASP.NET MVC site running at  I figured this would be a good candidate to throw in a container since it has no external dependencies, like databases, etc.  It runs on ASP.NET MVC 3 or 4 and dotnet framework 3.5.

For windows base containers there are two bases I have seen from Microsoft: Windows Core and Windows Nano.  Since I haven’t worked with Nano and understand it is really stripped down, I decided to start with Windows Core.

Since the site runs on IIS, I started with the Microsoft/IIS container. The container is a build of Windows Core with web server role added.   I started out with the following Dockerfile for my site which is located at the root of the solution.  I published the site to ./brettski4/pub/release/, so it may be easily mapped in the file.

#FROM microsoft/iis:latest

# copy compiled code into container
# build sent to /pub
RUN mkdir c:\\website
COPY .\\brettski4\\pub\\release c:\\website

WORKDIR c:\\website

# Add features
RUN powershell -Command Add-WindowsFeature -Name Web-Server; \
 Add-WindowsFeature -Name Web-Asp-Net

# Add sites
RUN powershell -NoProfile -Command Import-Module IISAdministration; \
 New-IISSite -Name "brettskicom" -PhysicalPath "c:\website" -BindingInformation "*:8088:"


ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]

It’s a really simple Dockerfile. It starts by using the microsoft/iis:latest container image.  It then makes a directory in the container for the site files (c:\\website). Copies those files from the local machine into the container’s new directory, and finally makes the workdir c:\\website, which isn’t really needed for this container.

I needed to add two features to the container, ASP.NET application service and DotNet 3.5 (which isn’t in the file above).  This is the first place I was tripped up as ASP.NET 3.5 cannot be loaded in this container, it’s isn’t an available option!  Either is Dotnet Framework 3.5.  An error is raised that the source files are not available.  I wasn’t sure where to get them on the web, or what exactly was needed to add them (WSIM, etc.).

So sought out a different container.  Looking through the containers in the Microsoft Docker Hub listing I came across microsoft/aspnet.  Which is tagged for two versions: 3.5 and 4.6.2 (which has a few flavors). Going for the microsoft/aspnet:3.5 container image I updated my Dockerfile to add in web-server and application service.

FROM microsoft/dotnet-framework:3.5

# copy compiled code into container
# build sent to /pub
RUN mkdir c:\\website
COPY .\\brettski4\\pub\\release c:\\website

WORKDIR c:\\website

# Add features
RUN powershell -Command Add-WindowsFeature -Name Web-Server; \
 Add-WindowsFeature -Name Web-Asp-Net45; \
 Invoke-WebRequest -Uri "" -Outfile "c:\ServiceMonitor.exe"

# Add sites
RUN powershell -NoProfile -Command Import-Module IISAdministration; \
 New-IISSite -Name "brettskicom" -PhysicalPath "c:\website" -BindingInformation "*:8088:"

EXPOSE 8088 

ENTRYPOINT ["C:\\ServiceMonitor.exe", "w3svc"]

This Dockerfile starts out the same but is a bit different when adding features.  We add Web-Server, and ASP.NET 4.5.  Since this container didn’t come with IIS, Microsoft didn’t add the ServerMonitor.exe executable, I decided to grab it from the GitHub repo and save it in the container.

I am still not 100% sure what ServerMonitor.exe is used for, though it seems to keep the container active after it starts.  From what I have read, people had added useless loops written in powershell to have some type of process running at the start of the container. Running ServiceMonitor.exe while in the container only says, Usage: ServiceMonitor.exe [service name]. So little help there.

So this was my Tuesday, in between other things, getting something to work inside a Windows container.  If you haven’t played with Docker yet, I strongly suggest that you do.  It is quite easy to get started with, and containerization is going to continue to grow as big if not bigger than Virtual Machines did.

The Container image built is in my Docker repository here.


Creating a bootable USB

I am always needing this info and so many articles are too long or in video form, so I created this abridged version.

Mount ISO image using OS or third party app like Virtual Clone Drive by Elaborate Bytes

For this run-through we assume the USB is mounted as E: and the ISO is mounted as F:


Assume the USB drive shows as Disk 2

  • Select disk 2
  • clean
  • create partition primary
  • select partition
  • active
  • format fs=exfat quick label=”mylabel”
  • exit

Make bootable:

I used /net60 as I was doing Server 2016 at the time

  • cd f:\boot
  • f:> bootsect /nt60 e:

Copy Files From ISO to USB:

  • robocopy f:\ e:\ /e /mt:24

Should be all good to go


You mentioned that you can improve with documentation.

Try this. Every time you use documentation, add or adjust a part of it that wasn’t right. If there is no documentation for something you are looking for, start some. You don’t need to document the entire thing, just a start with perhaps a summary or note on what you want to remember next time. With an iterative approach to documentation it will get done and stay more up-to-date.

So I read today:

  • Amazon Fire TV now supports 4K Ultra HD for true-to-life picture quality. Watch high-definition 1080p streams on Amazon Video, Netflix, YouTube, Hulu and more, even without a 4K TV.

Today is 7/1/2016. By 2018 it will be 16k or maybe 32k true-life picture.


Monday ——

8:05am User called to say they forgot password. Told them to use password retrieval utility called FDISK. Blissfully ignorant, they thank me and hang up. God, we let the people vote and drive, too?

8:12am Accounting called to say they couldn’t access expense reports database. Gave them Standard Sys Admin Answer #112, “Well, it works for me.” Let them rant and rave while I unplugged my coffeemaker from the UPS and plugged their server back in. Suggested they try it again. One more happy customer…

8:14 am User from 8:05 call said they received error message “Error accessing Drive 0.” Told them it was an OS problem. Transferred them to microsupport.

11:00 am Relatively quiet for last few hours. Decide to plug support phone back in so I can call my girlfriend. Says parents are coming into town this weekend. Put her on hold and transferred her to janitorial closet down in basement. What is she thinking? The “Myst” and “Doom” nationals are this weekend!

11:34 am Another user calls (do they ever learn?). Says they want ACL changed on HR performance review database so that nobody but HR can access database. Tell them no problem. Hang up. Change ACL. Add @MailSend so performance reviews are sent to */US.

12:00 pm Lunch

3:30 pm Return from lunch.

3:55 pm Wake up from nap. Bad dream makes me cranky. Bounce servers for no reason. Return to napping.

4:23 pm Yet another user calls. Wants to know how to change fonts on form. Ask them what chip set they’re using. Tell them to call back when they find out.

4:55 pm Decide to run “Create Save/Replication Conflicts” macro so next shift has something to do.


8:30 am Finish reading support log from last night. Sounded busy. Terrible time with Save/Replication conflicts.

9:00 am Support manager arrives. Wants to discuss my attitude. Click on PhoneNotes SmartIcon. “Love to, but kinda busy. Put something in the calendar database!” I yell as I grab for the support lines, which have (mysteriously) lit up. Walks away grumbling.

9:35 pm Team leader from R&D needs ID for new employee. Tell them they need form J-19R=9C9\\DARR\K1. Say they never heard of such a form. Tell them it’s in the SPECIAL FORMS database. Say they never heard of such a database. Transfer them to janitorial closet in basement.

10:00 am Perky sounding intern from R&D calls and says she needs new ID. Tell her I need employee number, department name, manager name, and marital status. Run @DbLookup against state parole board database, Centers for Disease Control database, and my Oprah Winfrey database. No hits. Tell her ID will be ready tonight. Drawing from the lessons learned in last week’s “Reengineering for Customer Partnership,” I offer to personally deliver ID to her apartment.

10:07 am Janitor stops by to say he keeps getting strange calls in basement. Offer to train him on Notes. Begin now. Let him watch console while I grab a smoke.

1:00 pm Return from smoking break. Janitor says phones kept ringing, so he transferred them to cafeteria lady. I like this guy.

1:05 pm Big commotion! Support manager falls in hole left where I pulled floor tiles outside his office door. Stress to him importance of not running in computer room, even if I do yell “Omigod — Fire!”

1:15 pm Development Standards Committee calls and complains about umlauts in form names. Apologizing for the inconvenience, I tell them I will fix it. Hang up and run global search/replace using gaks.

1:20 pm Mary Hairnet from cafeteria calls. Says she keeps getting calls for “Notice Loads” or “NoLoad Goats,” she’s not sure, couldn’t hear over industrial-grade blender. Tell her it was probably “Lettuce Nodes.” Maybe the food distributor with a new product? She thinks about it and hangs up.

2:00 pm Legal secretary calls and says she lost password. Ask her to check in her purse, floor of car, and on bathroom counter. Tell her it probably fell out of back of machine. Suggest she put duct tape over all the airvents she can find on the PC. Grudgingly offer to create new ID for her while she does that.

2:49 pm Janitor comes back. Wants more lessons. I take off rest of day.


8:30 am Irate user calls to say chipset has nothing to do with fonts on form. Tell them Of course, they should have been checking “Bitset,” not “chipset.” Sheepish user apologizes and hangs up.

9:10am Support manager, with foot in cast, returns to office. Schedules 10:00am meeting with me. User calls and wants to talk to support manager about terrible help at support desk. Tell them manager about to go into meeting. Sometimes life hands you material…

10:00 am Call Louie in janitorial services to cover for me. Go to support manager’s office. He says he can’t dismiss me but can suggest several lateral career moves. Most involve farm implements in third-world countries with moderate to heavy political turmoil. By and by, I ask if he’s aware of new bug which takes full-text indexed random e-mail databases and puts all references to furry handcuffs and Bambi Boomer in Marketing on the corporate Web page. Meeting is adjourned as he reaches for keyboard, Web browser, and Tums.

10:30 am Tell Louie he’s doing great job. Offer to show him mainframe corporate PBX system sometime.

11:00 am Lunch.

4:55 pm Return from lunch.

5:00 pm Shift change; Going home.


8:00 am New guy (“Marvin”) started today. “Nice plaids” I offer. Show him server room, wiring closet, and technical library. Set him up with IBM PC-XT. Tell him to quit whining, Notes runs the same in both monochrome and color.

8:45 am New guy’s PC finishes booting up. Tell him I’ll create new ID for him. Set minimum password length to 64. Go grab smoke.

9:30 am Introduce Louie the custodian to Marvin. “Nice plaids” Louie comments. Is this guy great or what?!

11:00 am Beat Louie in dominos game. Louie leaves. Fish spare dominos out of sleeves (“Always have backups”). User calls, says Accounting server is down. Untie Ethernet cable from radio antenna (better reception) and plug back into hub. Tell user to try again. Another happy customer!

11:55 am Brief Marvin on Corporate Policy 98.022.01: “Whereas all new employee beginning on days ending in ‘Y’ shall enjoy all proper aspects with said corporation, said employee is obligated to provide sustenance and relief to senior technical analyst on shift.” Marvin doubts. I point to “Corporate Policy” database (a fine piece of work, if I say so myself!). “Remember, that’s DOUBLE pepperoni and NO peppers!” I yell to Marvin as he steps over open floor tile to get to exit door.

1:00 pm Oooooh! Pizza makes me so sleepy…

4:30 pm Wake from refreshing nap. Catch Marvin scanning want ads.

5:00 pm Shift change. Flick HR’s server off and on several times (just testing the On/Off button…). See ya tomorrow.


8:00 am Night shift still trying to replace power supply in HR server. Told them it worked fine before I left.

9:00 am Marvin still not here. Decide I might start answering these calls myself. Unforward phones from Mailroom.

9:02 am Yep. A user call. Users in Des Moines can’t replicate. Me and the Oiuji board determine it’s sunspots. Tell them to call Telecommunications.

9:30 am Good God, another user! They’re like ants. Says he’s in San Diego and can’t replicate with Des Moines. Tell him it’s sunspots, but with a two-hour difference. Suggest he reset the time on the server back two hours.

10:17 am Pensacola calls. Says they can’t route mail to San Diego. Tell them to set server ahead three hours.

11:00 am E-mail from corporate says for everybody to quit resetting the time on their servers. I change the date stamp and forward it to Milwaukee.

11:20 am Finish @CoffeeMake macro. Put phone back on hook.

11:23 am Milwaukee calls, asks what day it is.

11:25 am Support manager stops by to say Marvin called in to quit. “So hard to get good help…” I respond. Support manager says he has appointment with orthopedic doctor this afternoon, and asks if I mind sitting in on the weekly department head meeting for him. “No problem!”

11:30 am Call Louie and tell him opportunity knocks and he’s invited to a meeting this afternoon. “Yeah, sure. You can bring your snuff” I tell him.

12:00 am Lunch.

1:00 pm Start full backups on UNIX server. Route them to device NULL to make them fast.

1:03 pm Full weekly backups done. Man, I love modern technology!

2:30 pm Look in support manager’s contact management database. Cancel 2:45 pm appointment for him. He really should be at home resting, you know.

2:39 pm New user calls. Says want to learn how to create a connection document. Tell them to run connection document utility CTRL-ALT-DEL. Says PC rebooted. Tell them to call microsupport.

2:50 pm Support manager calls to say mixup at doctor’s office means appointment cancelled. Says he’s just going to go on home. Ask him if he’s seen corporate Web page lately.

3:00 pm Another (novice) user calls. Says periodic macro not working. Suggest they place @DeleteDocument at end of formula. Promise to send them document addendum which says so.

4:00 pm Finish changing foreground color in all documents to white. Also set point size to “2” in help databases.

4:30 pm User calls to say they can’t see anything in documents. Tell them to go to view, do a “Edit — Select All”, hit delete key, and then refresh. Promise to send them document addendum which says so.

4:45 pm Another user calls. Says they can’t read help documents. Tell them I’ll fix it. Hang up. Change font to Wingdings.

4:58 pm Plug coffee maker into Ethernet hub to see what happens. Not (too) much.

5:00 pm Night shift shows up. Tell that the hub is acting funny and to have a good weekend.


I am not sure who originally wrote this, though I would really like to know.  I am thinking it may be Markus Baur (  I remember first seeing this around 1999, and being in system administration and support at the time I found it quite funny.  So much I have thought about it now again through the years.

“Lettuce Nodes”

The stats helper monkeys prepared a 2015 annual report for this blog.

Here’s an excerpt:

A New York City subway train holds 1,200 people. This blog was viewed about 6,700 times in 2015. If it were a NYC subway train, it would take about 6 trips to carry that many people.

Click here to see the complete report.

Another issue has arisen with my Windows 10 external boot on my Mac Book Pro.  There was an large update for Windows 10 which was failing. The reason, you can’t install windows on a usb flash drive using setup.  Sigh.

After a quick google search the fix was simple enough.  You simply go to registry key HKLM:\SYSTEM\CurrentControlSet\Control and change the value for PortableOperatingSystem from 1 to 0.

The part I didn’t expect is that now my MBP wants to boot the the external drive by default.  I am sure setup changed this in the boot partition.  My fear is bricking the MBP partition as I am not as familiar with its configuration.  I can still easily hold down the option key to cho0se the partition to boot to, though I don’t want to do this each time going into OSX as I use it 90% of the time now.

Under system preferences I opened Startup Disk, unlocked it and selected the Macintosh HD drive (the only one in the list by the way as my external drive is disconnected).  I then clicked the restart button.

Hey, that worked, cool.  The MBP now boots to OSX on restart or power-up, nice.

I have always known Microsoft OS’s to be a bitch with boot partitions (thinking back to old Linux multi-boot systems I had).  It has just been a while since I have been bit by it and I guess in a way I would have hoped they would have improved on things a bit.

%d bloggers like this: