Programming, philosophy, pedaling.

KBSecret 0.5.x: Packages, Typed Arguments, and More

Jun 23, 2017

Tags: ruby, programming, devblog, kbsecret

I released KBSecret 0.5.0 earlier today, followed in quick succession by 0.5.1 through 0.5.4 for refinements and minor bug fixes. This post will detail this big changes made since 0.4.x.

System-level Packages

Getting KBSecret built into common distro package formats has been a goal since the very beginning, but was hampered severely by the complexity and opacity of the deb and RPM formats. PKGBUILD for Arch is substantially simpler than both of them, but I don't currently run Arch on any of my desktop computers.

KBSecret's future looked increasingly bound to RubyGems for distribution... until I found fpm.

Not only does fpm provide a single interface for building debs, RPMs, and pacman packages (among others), but it can also automatically download the latest version of a gem from RubyGems and convert it into an installable package in a matter of seconds.

Oh, and it's written in Ruby. And gem install-able:

$ gem install fpm
$ fpm -s gem -t deb kbsecret
Created package {:path=>"rubygem-kbsecret_0.5.2_all.deb"}
$ ls *.deb

How awesome is that?

Of course, there is one downside: each KBSecret dependency needs its own package, meaning that a fully-working-package-manager-installed KBSecret requires (as of writing this) no less than 15 individual packages:

$ ls pkg/deb/rubygem-*

I'll be looking into ways to merge this into one or two "god" packages. Until that happens, sudo dpkg -i *.deb (or your manager of choice) should work just fine.

I haven't figured out how (or whether) I actually want to distribute these packages, since managing an apt repository seems like a bit of a pain. I may just end up hosting the built packages statically on my keybase.pub, bringing us full circle.

In the mean time, you can build the packages for yourself with the project's Makefile:

# packages will appear under ./pkg/<target>
$ bundle exec make deb
$ bundle exec make rpm
$ bundle exec make pacman

Trailing Argument Parsing and Typing

The vast majority of kbsecret commands have a signature like this:

kbsecret <command> <--options> <arguments>

The command is followed by options either in short (-s) or long (--session) format, with possible values (--foo bar), finally followed by one or more arguments.

KBSecret uses Slop for option parsing, which dumps the trailing arguments into the args array in the returned result.

This works just fine when when a command has only one argument, but quickly becomes ugly for variable-argument commands like kbsecret new:

kbsecret new <options> <type> <label> <field1 field2 ...>

which used to break down into something like this:

type, label = opts.args.shift 2

# some logic to validate the type
# some logic to validate the label

fields = opts.args

# some logic to ensure that fields were passed

I didn't really like how ad-hoc this was, and I couldn't find a good library for handling only trailing arguments. So I wrote my own, Dreck1.

Dreck takes the arguments remaining after a successful option-parsing, gives them names, and typechecks them:

args = Dreck.parse opts.args do
  string :type
  string :label
  list :string, :fields

args[:type] # => "login"
args[:label] # => "netflix"
args[:fields] # => ["bob", "hunter2"]

KBSecret currently only uses the string and list (of string) types, but Dreck provides many others. I also plan to add support for custom types at some point.

Since Dreck needs to leech off of the state returned by a successful Slop.parse call, I encapsulated both of them in instances of the new KBSecret::CLI class (previously a module).

Per-command argument parsing now looks like this:

cmd = KBSecret::CLI.new do
  slop do |o|
    o.bool "-f", "--foo", "toggle foo"

  dreck do
    string :bar

cmd.opts.foo? # => true
cmd.args[:bar] # => "baz"

This extra level of checking makes for some much nicer error messages:

$ kbsecret new
Fatal: Too few arguments given (0, expected >=2).

"And More"

I lied a bit. There only relatively minor stuff for 0.5.x releases besides packages and trailing argument parsing:

Thanks for reading!

- William

  1. I named it "dreck" in reference to the Yiddish word for garbage, but I've been told that it has a slightly more vulgar connotation in other Germanic languages.