Joe Maller.com

Vagrant NFS Shares without a password

Since I switched a few months ago, Vagrant has been humming along nicely, spinning up trim little Ansible-provisioned Ubuntu boxes as needed. Since I’m using Virtual Box as the provider and shared folders barely work with more than a handful of files, my active projects are made available as NFS share points. Running on OS X, Vagrant’s NFS shares are configured by modifying /etc/exports, and unfortunately, that requires administrator privileges and a password prompt.

Thankfully someone shared a workaround shell script which tweaked sudoers so vagrant up no longer required a password. It worked perfectly, until recently.

With the release of Vagrant 1.3, the NFS password prompt was back. The modified sudoers commands no longer worked.

Updating sudoers

All sudo commands are logged, so figuring out what changed was just a slightly clumsy matter of checking the logs with Vagrant 1.2.7, then installing Vagrant 1.3.x and looking for changes. This was a lot more effective than trying to step through the diffs of the Ruby code to reconstruct the various commands.

In previous versions of Vagrant whitelisting these commands allows editing of /etc/exports without a password:

/usr/bin/su root -c echo '*' >> /etc/exports
/usr/bin/sed -e /*/ d -ibak /etc/exports

In Vagrant 1.3.x, those commands were updated:

/bin/bash -c echo '*' >> /etc/exports
/usr/bin/sed -E -e /*/ d -ibak /etc/exports

Based on the original shell script, here is the block that needs to be added to /etc/sudoers for password-free startup with NFS shares:

Cmnd_Alias VAGRANT_EXPORTS_ADD = /bin/bash -c echo '*' >> /etc/exports
Cmnd_Alias VAGRANT_NFSD = /sbin/nfsd restart
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -E -e /*/ d -ibak /etc/exports
%staff ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_EXPORTS_REMOVE

I also posted an updated fork of the original workaround, install_vagrant_sudoers.sh:


Cosmos 2014

OMG.

A. I trust Neil deGrasse Tyson
B. My kids will be strapped to the sofa for this.

I remember watching the original with my dad. I hope this sequel is every bit as unabashedly hokey and fascinating as the original.

iTunes? Put Vangelis on repeat.


Comping with Web Fonts (you don’t need SkyFonts)

I wrote about problems downloading web fonts for desktop use when Google’s Web Fonts debuted, but years later it’s still an issue. There’s no way to anticipate the twists and turns a creative project will take. Designers should be free to play and experiment with typefaces without worrying about running out of time or exceeding a monthly usage cap. Convoluted, fragile workflows only create anxeity, they don’t foster creativity.

Monotype’s SkyFonts service is a nice idea, I guess, but their usage terms seem impractical and unrealistic. The few designers I know who’ve tried it found it restrictive and expensive.

The thing is, with a little knowledge of how web fonts work, using those typefaces in desktop apps is quite easy.

woff-ttf-to-otf

Web fonts are normally provided in three file formats, two of which, TrueType *.TTF and Web Open Font Format *.WOFF, are easily converted back to standard TrueType or OpenType fonts. Because of browser variation and font-face implementations, all three font containers are usually linked from a site’s stylesheets.

To use any web font for comping in desktop apps, just convert the ttf or woff file to otf, then use it like you would any normal font. There are a number of tools for converting fonts, but the following web sites work well enough that I didn’t bother downloading an app.

Standard-level Fonts.com accounts allow for self-hosted webfont projects. This means the font files can be downloaded and hosted on the web server alongside other assets like images or stylesheets. Having those files makes converting fonts for comping easy, but it’s just as simple to download the files with a web browser. Any file on the web can be copied, in fact every file viewed on the web already is a copy.

Yes, you can steal fonts using this. Don’t. “Good Artists Copy; Great Artists Steal” wasn’t about shoplifting.

Notes

  • Web TrueType fonts often won’t show in menus because their name-tables have been munged to discourage copying/theft. Converting fonts to a different container format makes them work correctly.
  • Some woff files didn’t work, in those rare cases the corresponding ttf worked fine–you might need to dig into the stylesheets to find the url.
  • Converted web fonts usually have messy names in menus.
  • Disable or remove any local copies of fonts when the mockup phase ends. Locally installed fonts can conflict with web versions and lead to maddening discrepencies in testing.

A few workflow links and examples

This morning I had a nice breakfast with a friend where various workflow and technology advances came up. This is really an email to him, but I thought it was worth sharing.

Composer and Packagist for PHP

Composer is a project-based dependency manager which makes it very easy to integrate PHP libraries into projects. Packagist is where you find which libraries are available, and has the simplest installation instructions. With Composer installed, adding features like Markdown or YAML parsing literally becomes as trivial as editing a json file.

Composer relies on modules being authored according to PHP Framework Interoperability Group’s PSR-0 standard. Since I’ve been guilty of bashing PHP, it’s probably worth mentioning Fabien Potencier’s post, PHP is much better than you think. PHP is far better than it used to be and the developer ecosystem is very, very active. (but I still prefer Python)

Homebrew

Homebrew is a package manager for OSX. Similar to apt-get or yum on Linux, this is the successor to MacPorts or Fink. It installs with one line pasted into the terminal (assuming you’ve got XCode already) and makes installing and removing packages ridiculously clean and easy. Homebrew is pretty much the first thing I install on a new Mac.

Forecast.io

The weather app Forecast.io is a terrific example of what’s possible with web apps that perform like native code. They’re also pushing a lot of cutting edge HTML 5 technologies. Startup times could be better, but that will come with hardware. Forecast is an offshoot of Dark Sky, my favorite iOS weather app.

Unreal on the web

Continuing the theme of what’s becoming possible on the web, Mozilla and Epic recently demoed the Unreal engine running in a browser. You’ll need Firefox Nightly for the best experience. The port uses HTML5, WebGL and JavaScript compiled from C source. This runs at 50+ frames per second from my laptop full screen on a second display. Crazy.


Undone

That moment when you realize the quickie side project has taken too long and even though it’s close to done, you put it aside to deal with the tasks that have piled up behind it. Knowing the likihood of completing the side project diminishes with every moment it’s left undone.


Solving a difficult KenKen puzzle

That right there is the hardest KenKen puzzle reviews I’ve ever come across. Maybe I’m just missing a strategy and there’s something obvious I overlooked, but I found the levels of ambiguity in this one to be very, very difficult to unravel. I’ve been coming back to this puzzle for months. Having finally cracked it, I wanted to document the solution. Partly to remember what happened, but mostly because it just felt so darn good to finally figure out.

With KenKen puzzles, there is no guessing. The puzzles are logical, and after doing hundreds of them over the past several years, I’ve only come across one or two that seemed to have equally forked possiibilities. And I hated those.

KenKen is similar to Sudoku, rows and columns contain n-digits with no repeats (a Latin Square). With KenKen, it can be helpful to know the sum of all digits in the puzzle’s dimension since every row and column must add up to that Triangular number. For a six digit puzzle this is 21. For this particular puzzle, it’s also worth remembering that there are only three paths to 11: {1, 4, 6}, {2, 3, 6} and {2, 4, 5}. Note the digits unique to each pathway.

Starting Out

The key to breaking into this one was column 6. Since column 6’s top cage sums to 11, and F6 is 5, the sum of D6 and E6 must equal 5. Since the only possibilities are combinations of 1, 2, 3 and 4, and E6 can’t be 1 because the sum of E5 and F5 can’t be 12, there are only three possibilities, D6: {1, 2, 3} and E6: {2, 3, 4}. Since the sum of both cages is 20 and we know 5 is accounted for, the sum of C3 through F6 must be 15. That means A5 and B5 have to add up to 6. The remaining half of that cage, B4 and C4 must add up to 7.

We now know the sum of B4 through F6, so A4 must be 3. This also reveals F3 is also 3 because the sums of all cells in the top two cages of column 3 are known.

Having a 3 in F3 means the remaining slots of that cage, F1 and F2 can only be 2 and 4. This means F4 must be 1 and F5 must be 6 because, like E6, F5 can’t be 1. Additionally, D4 and E4 must contain 4 and 6 because there’s no other way to get to 11 using a 1. The last two cells of column 4 can only be 2 and 5.

The upper left cage, A2, B1 and B2, has limited choices. Since the largest possible number is 6, the sum of any two adjacent sides can’t be less than 9. Likewise, since the largest adjacent sum is 11, neither A2 or B1 can be less than 4. The only exception is B2 which could be 3 since both adjacent cells could be 6.

But the D2, E2 cage can’t contain 3 and 4. If it did, then A2 and B2 could only be 5 and 6, which would mean B1 would have to be 4. The problem is that F1 and F2 can only be 2 or 4 and column 2’s 4 would already be taken by the 3 and 4 in D2 and E2. So, the D2, E2 cage can only be a combination of 2 and 5 or 1 and 6, the sum of A2, B2 must be 9 and B1 has to be 6.

At this point, things get hairy. I couldn’t see any obvious connection and essentially started trying alternate solutions.

A dead end

There are only two remaining combinations for reaching 11 in the A6, B6, C6 cage, but if A2 was 6 and B2 was 3, the only remaining combination would be {1, 4, 6} because the top row would already have 6, 1, and 3, leaving only a 2 or 4 as candidates in A6 except the 2 would be taken by any combination of A3, B3, B4, A5 and B5. So, from here, the top row could only be filled out one way:

That cascades down to E6. Now the D2, E2 cage has would have to be 2 or 5 because the 6 was taken by A2. But E6 couldn’t be 2 because then E5 would have to be 5 which would make E2 impossible. And here’s where this tangent falls apart all over the board. That was a long way to go to prove that A2 and B2 have to be 4 and 5.

It all comes together

Disproving that was painful, but now we’re starting to get somewhere. Confirming A2‘s and B2‘s candidates reveals C2 is 3, F2 is 2 and F1 is 4. This also limits C1‘s candidates to 2 and 5. C4 is also limited to 2 and 5, so no other cells in row C can contain a 2 or 5.

Even better, there’s now only one way to fill in the top two rows:

That pretty much gives it all away. Columns 2, 4 and 6 are quickly revealed and the remaining values are easily discovered. Feel free to visit Oranum review.

The solved puzzle:

Solvers

It was interesting and humbling to run this puzzle through two KenKen solvers after finishing it by hand.

Michael Heyeck’s NekNek solver solved the puzzle instantly. Michael documented the creation of his KenKen solver, including the complete python source code (less than 150 lines with comments).

Below is the puzzle in JSON notation for the Poison Meatball solver. It was slightly reassuring that this took a few seconds to get to the answer.

{
    "width": 6,
    "height": 6,
    "values": [1, 2, 3, 4, 5, 6],
    "rules": [{
        "op": "=",
        "value": 1,
        "cells": [{"x": 0, "y": 0 }]
    }, {
        "op": "+",
        "value": 15,
        "cells": [{"x": 1, "y": 0 }, {"x": 0, "y": 1 }, {"x": 1, "y": 1 }]
    }, {
        "op": "+",
        "value": 10,
        "cells": [{"x": 2, "y": 0 }, {"x": 3, "y": 0 }, {"x": 2, "y": 1 }]
    }, {
        "op": "+",
        "value": 13,
        "cells": [{"x": 4, "y": 0 }, {"x": 3, "y": 1 }, {"x": 4, "y": 1 }, {"x": 3, "y": 2 }]
    }, {
        "op": "+",
        "value": 11,
        "cells": [{"x": 5, "y": 0 }, {"x": 5, "y": 1 }, {"x": 5, "y": 2 }]
    }, {
        "op": "+",
        "value": 13,
        "cells": [{"x": 0, "y": 2 }, {"x": 1, "y": 2 }, {"x": 0, "y": 3 }, {"x": 0, "y": 4 }]
    }, {
        "op": "+",
        "value": 11,
        "cells": [{"x": 2, "y": 2 }, {"x": 2, "y": 3 }, {"x": 2, "y": 4 }]
    }, {
        "op": "+",
        "value": 7,
        "cells": [{"x": 4, "y": 2 }, {"x": 4, "y": 3 }, {"x": 5, "y": 3 }]
    }, {
        "op": "+",
        "value": 7,
        "cells": [{"x": 1, "y": 3 }, {"x": 1, "y": 4 }]
    }, {
        "op": "+",
        "value": 11,
        "cells": [{"x": 3, "y": 3 }, {"x": 3, "y": 4 }, {"x": 3, "y": 5 }]
    }, {
        "op": "+",
        "value": 13,
        "cells": [{"x": 4, "y": 4 }, {"x": 5, "y": 4 }, {"x": 4, "y": 5 }]
    }, {
        "op": "+",
        "value": 9,
        "cells": [{"x": 0, "y": 5}, {"x": 1, "y": 5}, {"x": 2, "y": 5}]
    }, {
        "op": "=",
        "value": 5,
        "cells": [{"x": 5, "y": 5}]
    }]
}