Fly CLI: buildGoPackage with bash completions

I’m writing a derivation for the fly CLI (for use with Concourse CI).

I wanted to include bash completions along with the fly executable. Because buildGoPackage produces a multiple-output package, I ended up using symlinkJoin to wrap things up in a single derivation.

I’m not familiar with golang, and it was a bit of a journey figuring out how to package the bash completions alongside the binary. I’d like to post what I ended up with in case anyone has suggestions about possible improvements.


At the moment, I’m accessing the derivation through an overlay:

self: super: {
  fly = super.callPackage pkgs/fly { };
}

The pkgs/fly/default.nix:

{ pkgs ? import ./nixpkgs.nix { } }:

let
  pinned = builtins.fromJSON (builtins.readFile ./pinned.json);

  fly = with pkgs; buildGoPackage rec {
    name = "fly-${version}";
    version = pinned.fly.version;
    src = fetchFromGitHub pinned.fly.src;

    goPackagePath = "github.com/concourse/fly";
    goDeps = ./deps.nix;

    buildFlagsArray = ''
      -ldflags=
        -X ${goPackagePath}/version.Version=${version}
    '';

    postInstall = ''
      install -D -m 444 ${
        pkgs.writeText "fly.bash" (builtins.readFile ./fly.bash)
      } $out/share/bash-completion/completions/fly
    '';
  };

in pkgs.symlinkJoin {
  name = "fly";

  paths = [
    fly.bin
    fly.out
  ];
}

The pkgs/fly/fly.bash script:

#!/usr/bin/env bash

_fly() {
    # All arguments except the first one
    args=("${COMP_WORDS[@]:1:$COMP_CWORD}")

    # Only split on newlines
    local IFS=$'\n'

    # Call completion (note that the first element of COMP_WORDS is
    # the executable itself)
    COMPREPLY=($(GO_FLAGS_COMPLETION=1 ${COMP_WORDS[0]} "${args[@]}"))
    return 0
}

complete -F _fly fly

The pkgs/fly/deps.nix was generated using go2nix as follows:

git clone --jobs=8 --recursive --branch=v4.2.3 https://github.com/concourse/concourse
cd concourse
nix-shell -p go go2nix
export GOPATH=$PWD
cd src/github.com/concourse/fly
go build -ldflags "-X github.com/concourse/fly/version.Version=4.2.3"
go2nix save

As a side-note, I’m using buildGoPackage rather than buildGoModule because the version of fly I need doesn’t use go modules. I’d like to eventually submit a derivation to nixpkgs, though I’ll probably try to package a more recent version of fly for that, and would put something more idiomatic together (e.g. without pinned.json, etc.).