Some might be confused by the changes to Fabric in 2.x. It's a complete rewrite and a complete API redesign.

Upload binary data as file-like object

c = fabric.Connection('')
f = io.BytesIO(b"some initial binary data: \x00\x01")
c.put(f, 'out.bin')

Use plaintext authentication details

password = 'xyzzy'
connect_kwargs = {'password': password}
c = fabric.Connection('', connect_kwargs=connect_kwargs)'/bin/true')

Bundled sudo operations as part of file transfer

In the words of the Fabric developers,

This was one of the absolute buggiest parts of v1 and never truly did anything users could not do themselves with a followup call to sudo, so we opted not to port it.

This mouthful will do it:

import hashlib
import fabric
import shlex

class RemoteTemporaryPath(object):
    # public name attribute designed to be retrieved by user
    name = None

    def __init__(self, c, remote_path):
        hasher = hashlib.sha1()
        hasher.update(remote_path.encode('utf-8')) = hasher.hexdigest()

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):"rm -rf {}".format(shlex.quote(

def put_with_sudo(c, local_path, remote_path):
    with RemoteTemporaryPath(c, local_path) as r:
        c.sudo("mv {} {}".format(shlex.quote(, shlex.quote(remote_path)), password='xyzzy')

c = fabric.Connection('')
put_with_sudo(c, "", "/etc/")

A very important thing here: you need to decide at script time whether you will respond to the sudo authentication prompt by hand or if you will use some other method to get the sudo password. Here we are taking the approach of "use some other method". If you need to respond to the sudo prompt by hand, you need to use"sudo foo") instead of c.sudo("foo", password='bar'). NB: I don't claim this code is 100% safe, please be careful.

Posted 2019-03-05

You face a problem where you want to remove a leading 'container' directory from an archive when extracting. This can happen when you're using the Puppet archive resource from puppetlabs-archive module.

archive { '/tmp/':
    extract => true,
    extract_path => '/srv/http/omeka_s',
    source => ''

Here we want to extract directly into /srv/http/omeka_s so that the index.php is created inside that directory. Problem is that the file contains an embedded directory, omeka-s. Note that it doesn't quite match our desired one, so we can't do the trick of extracting to /srv/http and relying on the archive structure to create the desired directories. [That's probably wise, as that approach is somewhat fragile.]

If it was a tar file, we could use tar --strip-components=1, but as it's a zip file we can't do this. Look on the net and you'll find many asking this question. It's 2019 and we still don't have a good solution for extracting archives without caring about the format. Well, there are two...

dtrx -- "do the right extraction" works well, except it's designed for the opposite of this situation. It's designed for avoiding tarbombs. In fact, we have a non-tarbomb when we actually desire a tarbomb. Still, it's worth a mention as it's packaged for many distributions.

7zip doesn't work because it does the equivalent of -j when using the -r option.

So here's my hacky python workaround. I was really hoping not to have to do this, and to be able to recommend a solution that was already packaged in Debian, but I couldn't find one, so this will have to do for now.

#! /usr/bin/env python3

import sys
import zipfile
import os
import shutil

strip_n = int(sys.argv[1])
zipfile_path = sys.argv[2]

with zipfile.ZipFile(zipfile_path) as the_zip:
    namelist = the_zip.namelist()

    for member in namelist:
        fixed_path = os.path.normpath(member)
        components = fixed_path.split(os.sep)

        if len(components) < strip_n:
            raise Exception('unexpected number of components in filename')

        stripped = components[strip_n:]
        target_path = os.path.join(*stripped)

        upperdirs = os.path.dirname(target_path)
        if upperdirs and not os.path.exists(upperdirs):

        with as source, open(target_path, "wb") as target:
            shutil.copyfileobj(source, target)
Posted 2019-02-28

The first thing to do is to create your combo box. If you try to do the simple zero argument constructor, you're going to get a box with zero choices, a blank dropdown. This is what happens when you use the QStandardItemModel, which is empty by default. You can choose either to use the QStandardItemModel or create your own subclass of QAbstractItemModel.

QComboBox* comboBox = new QComboBox;

Remember that a combo box represents a choice between several items of a list. So you need some way to specify that list. You have a choice of abstraction levels here. The simplest way is to use the QStandardItemModel directly, via the convenience methods provided by QComboBox. In order of complexity your other options are.

  • Use QStringListModel directly
  • Subclass QStringListModel
  • Subclass QAbstractListModel
  • Subclass QAbstractItemModel

Using QStringModel directly is simple.

QStringList list = {"foo", "bar", "baz"};
QAbstractItemModel* myModel = new QStringListModel(list, comboBox);

What if we want to set the selected item?


This will set the box to select bar, given the model above. That is, indices are zero-based.

Posted 2019-02-27

The puppet agent is not available in the base repository for the CentOS 7 release.

Puppet 3.6.2 is available in the EPEL repository. This is somewhat painfully old. Puppet 5.5.10 will debut in Debian Buster. The IUS repository does not feature packages for this either.

yum install ruby will give you Ruby by default.

Install in this order; facter; hiera; puppet. It will all go to /usr, for some reason that is a mystery to me.

Test it out:

[amoe@localhost bin]$ puppet --version

Check that we can access our puppet server.

[amoe@localhost bin]$ ping -c 1
PING ( 56(84) bytes of data.
64 bytes from ( icmp_seq=1 ttl=63 time=29.7 ms

--- ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 29.778/29.778/29.778/0.000 ms

Modify your hostname to something sensible, /etc/hostname. Also add an alias to the hostname into /etc/hosts.

Most files will install to /opt/puppetlabs by default.

Also install bind-utils for the host program that is required by Facter.

Posted 2019-02-25

These are my endgame saves from X3 Reunion, Terran Conflict, and Albion Prelude.

Posted 2019-02-02

This is an example of a technique that's shown in the Vue manual, basically you maintain two pieces of state, use one as the backing store and use a watcher to update the derived state using tweening mechanisms such as TweenLite in this case. Shown with a few hacks that are necessary to get it working with the d3-cluster node representation.

    <svg viewBox="0 0 1024 768" width="1024" height="768" 
      <circle v-for="descendant in tweenedDescendants"
              stroke="red" fill="grey"/>

    <button v-on:click="relayout">Relayout</button>

<script lang="ts">
import Vue from 'vue';
import {hierarchy, cluster, HierarchyNode} from 'd3';
import _ from 'lodash';
import {TweenLite} from 'gsap';

const familyData = {
    "name": "Eve",
    "children": [
            "name": "Cain"
            "name": "Seth",
            "children": [
                    "name": "Enos"
                    "name": "Noam"
            "name": "Abel"
            "name": "Awan",
            "children": [
                    "name": "Enoch"
            "name": "Azura"

interface FamilyDatum {

function initialCluster(val: any): HierarchyNode<FamilyDatum> {
    const theHierarchy = hierarchy(val);
    const clusterLayout = cluster();
    clusterLayout.size([1024, 768]);
    return theHierarchy;

export default Vue.extend({
    data() {

        return {
            hierarchyRoot: initialCluster(familyData) as HierarchyNode<FamilyDatum>,
            tweenedHierarchyRoot: initialCluster(familyData) as HierarchyNode<FamilyDatum>
    mounted() {
        window.setInterval(this.relayout, 1000);
    methods: {
        relayout() {
            const clusterLayout = cluster();

            // these hacks necessary because cluster() itself does some array
            // mutations that fool the reactivity
            const clonedHierarchy = _.clone(this.hierarchyRoot);
            clusterLayout.size([Math.random() * 1024, Math.random() * 768]);

            this.hierarchyRoot = clonedHierarchy;
    computed: {
        descendants(): HierarchyNode<FamilyDatum>[] {
            return this.hierarchyRoot.descendants();
        tweenedDescendants(): HierarchyNode<FamilyDatum>[] {
            return this.tweenedHierarchyRoot.descendants();
    watch: {
        // No deep watch needed because we always reassign the whole
        // array
        hierarchyRoot(val, oldVal) {
            console.log("inside handler function");
            // We know that descendants() function returns references to
            // the nodes that can be directly modified by mutation.
            // Because the x and y values have already been declared in the
            // data() method  -- that is, cluster() was already called once
            // to add the properties -- Vue knows that it should react to changes
            // on these properties.

            const targetDescendants = val.descendants();
            const tweenedDescendants = this.tweenedHierarchyRoot.descendants();

            for (var i = 0; i < tweenedDescendants.length; i++) {
                const node = tweenedDescendants[i];

                const targetX = targetDescendants[i].x;
                const targetY = targetDescendants[i].y;

      , 0.5, {x: targetX, y: targetY});

<elyts lang="less">

This is based on a technique from the Vue manual.

Posted 2019-01-06

The basic procedure is as follows: You create an interface RootState that defines your fields. You probably already have types for these various data items. You may have been previously forced to cast your bottom values to avoid ending up as never[], etc. So create an interface representing these values. Then refactor your store to type-check correctly when the object is assigned to a variable fo type StoreOptions<RootState>.

Then you derive another type as such.

import { Module } from 'vuex';

// ...

const graphView: Module<GraphViewState, RootState> = {
   // ...

Move all the previous contents of RootState into GraphViewState (in this example). RootState itself should become an empty interface.

Now you can include this in the modules key when constructing your Vuex store.

const store: StoreOptions<RootState> = {
    modules: { graphView }

export default new Vuex.Store(store);
Posted 2018-12-23

Here is a simple component for Vue/TS that demonstrates using the transition-group directive to collapse the width of elements when they are removed from a list.

One catch is that an unlabelled 'div' is inserted as the parent of all list items. This can cause problems when styling things using flexbox, which treats only its direct children as layout items. Also, if you have styling applied to the children, applying styling to the transition classes won't override those styles, because the transition style may be less specific than the styles on the child elements.

  <div class="home">

    <div class="container">
      <transition-group name="mytrans" tag="div">
        <div v-for="(box, index) in boxes"
          <button v-on:click="hide(index)">Hide</button>

<script lang="ts">
import Vue from 'vue';
import _ from 'lodash';

interface Box {
    isVisible: boolean;
    id: number;

export default Vue.extend({
    name: 'home',
    data() {
        return {
            boxes: [] as Box[]
    components: {},
    created() {
        for (var i = 0; i < 5; i++) {
            this.boxes.push({isVisible: true, id: i});
    methods: {
        hide(index: number): void {
            this.boxes[index].isVisible = false;

.container {
    height: 200px;
    background-color: blue;

/* Because vue transition creates a wrapper div around the transition'ed elements,
   we need to style that div to layout the elements correctly. */
.container > div {
    display: flex;
    flex-direction: row;

.box {
    height: 150px;
    width: 150px;
    margin: 16px;
    background-color: green;

.mytrans-leave-active {
    transition: all 0.5s ease-in;

.mytrans-leave-to {
    width: 0px;
Posted 2018-12-18

Setup procedure

Install git, this provides the git-http-backend. yum install git

Install httpd. yum install httpd

SetEnv GIT_PROJECT_ROOT /srv/git
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

<Files "git-http-backend">
    AuthType Basic
    AuthName "Git Access"
    AuthUserFile /srv/git/.htpasswd
    Require expr !(%{QUERY_STRING} -strmatch '*service=git-receive-pack*' || %{REQUEST_URI} =~ m#/git-receive-pack$#)
    Require valid-user

Create the git root and configure the users

mkdir /srv/git
htpasswd -c /srv/git/.htpasswd db57

Test procedure

On the server:

cd /srv/git
mkdir my-test-repository
cd my-test-repository
git init --bare
chown -R apache:apache /srv/git

On a client:

mkdir my-test-repository
cd my-test-repository
git init
echo "test" >
git add -A
git commit -m 'initial import'
git remote add origin http://localhost/git/my-test-repository
git push -u origin master

As far as I know, all repositories need to be created server-side before you are allowed to push to them.

From outside

Where the IP of your server is,

git clone

Cloning is unauthenticated, only push is authenticated.

Posted 2018-11-29

An extremely minimal way to publish your Vue component. You already have your project. I assume and hope you are using vue-cli 3.0. If not, immediately switch to it, vue create mypackage and port your project into the new structure.

To get the build for your project, you use the special lib build target to the vue-cli-service build subcommand. You can invoke this as such:

amoe@cslp019129 $ ./node_modules/.bin/vue-cli-service build --target lib --name amoe-butterworth-widgets src/components/TaxonSelect.vue

Here, amoe-butterworth-widgets is the name of the library that you intend to publish it. In this case, I'm publishing it as an unscoped public package, this is just the regular form of npm publishing that you all know and (hah) love.

TaxonSelect.vue will be exposed as the default export of the build module.

The build will produce several files under the dist subdirectory. You are looking for the UMD build. You'll find a file dist/amoe-butterworth-widgets.umd.js. Now you need to add a key to package.json.

    "name": "amoe-butterworth-widgets",
    "main": "dist/amoe-butterworth-widgets.umd.js",
    "version": "0.1.0",
    "license": "MIT",

It's wise to set a license and to obey semver as appropriate.

Now you need to be logged in before you can actually publish. Run npm adduser.

Once you've done this, simply run npm publish. Your package will be pushed up and made available at, where mypackage was specified in the name field of package.json.

When someone runs npm install mypackage, they'll get what is more-or-less a copy of your source tree. As far as I can see, npm doesn't attempt to clean your tree or reproduce your build in any way. So make sure that anything you don't want to be public is scrubbed before running npm publish.

When the user wants to actually use your component, TaxonSelect.vue is the default export, as mentioned above. So to use it, they just type import TaxonSelect from 'mypackage', and TaxonSelectis then a component that they can register in their components object. There are ways to export multiple components from a module, but that's outside the scope of this article.

Posted 2018-11-21
Emoji Representations
Posted 2018-09-14
Thoughts on Cheesesteak & More
Posted 2018-08-29
Vue + GraphQL + PostgreSQL
Posted 2018-07-20
Neo4j Cypher query to NetworkX
Posted 2018-05-09
FP & the 'Context Problem'
Posted 2018-02-27
Cloake Vegetable Biryani
Posted 2018-02-25
FFXII Builds
Posted 2018-02-02
Custom deployments solution
Posted 2017-12-09
SCons and Google Mock
Posted 2017-11-30
Sunday Lamb Aloo
Posted 2017-11-19
centos 6 debian lxc host
Posted 2017-11-03
Srichacha Noodle Soup
Posted 2017-10-17
Kaeng Kari
Posted 2017-10-13
Ayam Bakar (Sri Owen)
Posted 2017-10-12
Pangek Ikan (Sri Owen)
Posted 2017-10-12
Chicken Tikka Balti Masala
Posted 2017-10-06
Clojure Log Configuration
Posted 2017-09-28
Clojure Idioms: strict-get
Posted 2017-09-28
Posted 2017-09-18
Philly Cheesesteak
Posted 2017-09-14
Posted 2017-09-13
Srichacha Kaeng Pa
Posted 2017-08-31
Malaidar Aloo
Posted 2017-08-10
BBQ Balti Chicken
Posted 2017-07-19
Sabzi Korma
Posted 2017-07-18
Vegetable Tikka Masala
Posted 2017-07-02
Soto Ayam
Posted 2017-06-08
Bombay Aloo w/Bunjarra
Posted 2017-06-03
Chicken Dopiaza
Posted 2017-06-01
LJ Bunjarra
Posted 2017-05-31
Glasgow Lamb Shoulder Tikka
Posted 2017-05-24
Tofu Char Kway Teow
Posted 2017-05-12
King Prawn Balti
Posted 2017-04-24
Ad-hoc Quorn Rogan Josh
Posted 2017-04-15
Glasgow Vindaloo
Posted 2017-03-28
Posted 2017-03-26
Toombs Saag Balti
Posted 2017-02-25
Glasgow Bombay Rogan Josh
Posted 2017-02-21
Glasgow Chicken Balti
Posted 2017-02-16
Quorn Balti & Cloake Naan
Posted 2017-02-03
Two Spice Marinades
Posted 2017-01-18

This blog is powered by coffee and ikiwiki.