Tiddlywiki vs Jekyll for PKB

Due to increasing browser security and it having become abandonware, I’m going to move away from Tiddlywiki Classic. I tried using it in a .hta file, but IE has some problems with its JavaScript and I’m unwilling to fix the source myself.

I need two distinct personal knowledge bases: work-related and personal. I’d like to be able to edit each from my work laptop and my personal laptop, at the office and at home.

For a variety of (undocumented) reasons, I’ve narrowed my options to Tidddlywiki 5 (node.js edition) and a Jekyll-generated web site.

Some features I want:

  TW Support Jekyll Support
Inter-wiki linking Y Y
GFM Markdown Y Y
MathJax * 1 * 2
Images Y * 3 Y * 3
Send my wife a URL Y Y
Usable at work and home Y Y
Need not store my personal data in the cloud Y Y
Can search with my native OS search (e.g. Spotlight) Y Y
Export/migrate if support vanishes Y Y
Maintains a history of changes N * 4 N * 4
Dual-master support Y * 5 Y * 5
Easy up and running when I rebuild my machine N N
Edit/view from Mac, Windows, Linux Y Y
Reduced tool count (I’m using Jekyll for other sites). N Y
This page referenced by… Y N
Edit this page Y N * 6
  • 1 Tiddlywiki: http://mathjax-tw5.kantorsite.net/ renders in text/x-marked, but throws an error. This one fails too: https://gist.github.com/kpe/cc0547b318e6f8d4ddaa. Some debugging required.
  • 2 Jekyll: https://alan97.github.io/random/mathjax/ has a tutorial for MathJax.
  • 3: May have to manually put images into a local folder and link to them from the Markdown.
  • 4: Neither product maintains a change history. You’d have to use something like git to version your data.
  • 5: I have a Mac laptop and a Linux server at home. I want to be able to edit and view data when either is getting rebuilt. I can do this with either by using my Linux server as a git server.
  • 6: I think I could write something to do this for Jekyll with an edit button to link to the corresponding admin interface edit page.

=====

If I put them only on my linux server, I can access them from anywhere EXCEPT offline, and I can’t index them on my Mac. I can’t share with my wife if it is only on my Macbook. I can’t put my work PKB on my Macbook because I have no network connection between the two. I could use git to sync laptops with Linux server, and edit on either side. Need to make this low-friction.

Use Jekyll on as my PKB. That reduces my tool count, but when I’m looking at a page in Jekyll, navigating to EDIT it is a bother. Could I add an “Edit Me” button that is visible only locally? (Asked this on StackOverflow.) I’d use git-with-github for the public sites and git-with-mac-and-boxtop for the local sites.

Tagging is better in TW than Jekyll, but it exists in Jekyll.

Spotlight will find two copies of every page in Jekyll - one for the markdown and one for the HTML, unless I tell it not to index one of the folders.

If I use Tiddlywiki, it is very easy to mess it up or break part of it. I’ll need to use git agressively, so I can roll back changes. Sometimes rolling back a change seems to require stopping/starting the server (on my Mac): }

Reset Prius Oil Maintenance Required Warning

At some point, Toyota changed the oil-change interval on my 2010 Prius from 5,000 miles to 10,000 miles, but they didn’t update the firmware, so it still shows “Oil Maintenance Required” after 5000. Here’s how to reset the warning:

  • Power-on the car.
  • Press the “Trip” button until “TRIP A” is displayed.
  • Power-off the car.
  • Hold the Kilometers/MPH button down (like a keyboard Shift button).
  • Hold the brake pedal down.
  • Hold the power-on button down until it resets.
  • Release everything.

Too Much "Security"

Today, I wanted to download my financial accounts into Quicken. I entered my password to login to my Macbook. (My Macbook disk is encrypted, so all passwords after this point are silly.)

  • I booted my Windows VM. That Windows has a password, but Windows is willing to remember it.
  • I launched Quicken and it demanded a Quicken account password, ‘for you security.’
  • I launched my password wallet and it demanded a password.
  • I clicked the button in Quicken to download data, and it demanded the password for my password wallet.

This is not security. This is idiocy.

Why I'm Abandoning Clojure

Clojure is a lovely language idea. It is elegant. It is clever.

Clojure is an abysmal language reality for my purposes.

  • 35 lines of java dump for a syntax error? It makes me miss the original Wirth Pascal compiler, which seemed to have the singular error message “Syntax error, possibly missing semicolon on line above.”
  • I really, really want compile-time type checking on my parameters.
  • If I run my program via “lein run”, I have no trouble reading a data file. If I package it up into an uberjar, the code fails. I’m supposed to master class loaders just to be able to read a file when I package my app as a jar? I’m sure that Clojure and Java are doing something that is absolutely essential for somebody, but the environment is making simple stuff hard, and I won’t tolerate that.
  • I insist on an interactive debugger. You can actually get one via the Cursive IDE, and you can sorta, kinda get one via a plugin for Visual Studio Code. The Cursive one actually works… until it freaks out and you have to either restart the REPL or you have to do something called “Invalidate Caches/Restart”. (There’s no real indication whether your observed bad behavior is a bug in your code or if you need to invalidate caches. It’s just a Hail Mary you try when you don’t like the results you’re getting.) The plugin for Visual Studio Code – I’m sure it works for its developer, but I really tried to make it work. Maybe someday. If the language doesn’t have an interactive debugger, it’s just plain immature in my book.
  • I want to be able to code a ‘premature return’ from a function. Yeah, I know it’s ‘unnecessary.’ But I’ll tell you, it really simplifies real-world code when I can code if-exceptional-condition-return-error. The Clojure way seems to be to just issue a stack trace on bad data.
  • There’s an awful lot of cognitive overhead for an occasional programmer. In addition to the language itself, you’ve got Leiningen and a baroque directory structure to re-master, every time you take 6 months off from programming. It reminds me of J2EE, and that’s the epitome of excess cognitive overhead.

Firefox Portable Incredibly Slow

Firefox Portable Launches and Loads Very Slow

Firefox Portable started taking forever to launch and load. I was running it from a USB flash drive, so I suspected drive problems. That wasn’t it.

I only use Firefox Portable to run an old Tiddlywiki Classic, with the Tiddlyfox extension. Clicking in the search box would often provoke “not responding” with a white/gray browser window, for long periods of time. Closing Firefox could take 10 minutes to complete. Launching Firefox with TiddlyWiki could take 10 minutes.

Something was making my prefs.js file huge (as in 300 MB). When I tried to open in from Notepad, it brought my machine to a crawl, even after I copied the file to C:\Temp, so I knew it wasn’t a flash drive problem.

Something was adding millions of “E:\FirefoxPortable\FirefoxPortable” to my prefs.js file. I could look at a history of that file and see it grow by KB per day. (My flash drive is E:.)

The solution:

  • Close (and kill, if necessary) all Firefoxes (portable and installed).
  • Delete E:\FirefoxPortable\Data\profile\prefs.js
  • Launch Firefox Portable. It will offer to download an update. Tell it NO.
  • Tools/Options (alt-t)
  • Press Alt-Shift-N to “Never check for updates”. (You must use Alt-Shift to acces an accelerator key on the Options tab.)
  • Exit and re-launch Firefox Portable, opening your TiddlyWiki.
  • Click the TiddlyFox icon and perma-enable saving changes
  • Save changes.

Checklist in LibreOffice

Sometimes I need an outline/bulleted list which looks like a checklist. Here’s how to create a checkbox bullet in LibreOffice (on the Mac).

  • Create an ordinary bulleted list. (Use the bullets tool on the toolbar.)
  • Put the insertion point (i-beam cursor) at the beginning of the text of the line with the bullet you want to turn into a checkbox.
  • Format > Bullets and Numbering > Options > Select… > Font = Wingdings
  • I like the the first empty box in the list. (End of the 5th row on my display.) Character value Unicode F06F.

Tiddlywiki Without Docker On Macbook

Tiddlywiki on Node.js Without Docker on My Macbook

Here’s my how-to for how I set up plain old Tiddlywiki on my Macbook. My plan is to

  • Use the ‘Node.js’ TW natively (without Docker, without the MultiUser plugin). Soak. Get really comfortable with it.
  • Add MultiUser plugin. Soak. Get really comfortable with it.
    • Documentation for the plugin is here: https://github.com/OokTech/TW5-MultiUser
  • Wrap it in Docker.

That way, if there is weirdness, I’ll recognize which piece of the puzzle to blame.

Note: node.js version 9.7.1 for Darwin was installed from https://nodejs.org/download/release/ on my machine before I started this process.


First, define some constants in .bash_profile, to reduce redundant typing:

export twd='/Users/kevin/Sync/Sites/tw-plain'
alias twd="cd $twd"

Then

. ~/.bash_profile 
mkdir $twd
cd $twd

Create runme.sh containing:

#!/bin/sh
# Abort immediately on shell errors
set -e

TWD=/Users/kevin/Sync/Sites/tw-plain
cd $TWD

# Ensure Tiddlywiki is installed.  Peg the version.  I don't want it changing by surprise.
if [ ! -d node_modules/tiddlywiki ]; then
  echo "Installing tiddlywiki node package locally. This will take several minutes..."
  npm install tiddlywiki@5.1.15
fi

#This is how you would initialize a non-plugin wiki
if [ ! -d $TWD/mywiki ]; then
  /usr/local/bin/node $TWD/node_modules/tiddlywiki/tiddlywiki.js mywiki --init server
fi

#if [ ! -d node_modules/tiddlywiki/editions/MultiUserWiki ]; then
#
#  echo "Installing tiddlywiki node package locally. This will take several minutes..."
#  npm install tiddlywiki@5.1.15
#
#  cd $TWD/node_modules
#  echo "CLONING MultiUser plugin"
#  git clone --depth=1 https://github.com/OokTech/TW5-MultiUser.git tiddlywiki/plugins/OokTech/MultiUser

#  echo "Copying starter wiki"
#  cp -r tiddlywiki/plugins/OokTech/MultiUser/MultiUserWiki tiddlywiki/editions/
#fi

cd $TWD

echo "Launching node"
#exec /usr/local/bin/node ./node_modules/tiddlywiki/tiddlywiki.js editions/MultiUserWiki  --wsserver 8080 ${USERNAME:-user} ${PASSWORD:-'wiki'} 0.0.0.0
exec /usr/local/bin/node $TWD/node_modules/tiddlywiki/tiddlywiki.js mywiki --server 8080 $:/core/save/all text/plain text/html 127.0.0.1

Be sure to: chmod +x runme.sh

Launch your wiki with:

./runme.sh

The first time you launch, you may see the following errors. They can safely be ignored:

npm WARN saveError ENOENT: no such file or directory, open '/Users/kevin/Sync/Sites/tw-plain/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/Users/kevin/Sync/Sites/tw-plain/package.json'
npm WARN tw-plain No description
npm WARN tw-plain No repository field.
npm WARN tw-plain No README data
npm WARN tw-plain No license field.

Note that if you want to start from a clean slate, you’ll find it handy to have a script. Create a file named make-clean.sh:

#!/bin/bash
echo "Run this only if you want to delete all the wiki content and start over. Press control-C to stop or Enter to continue."
read

rm -rf mywiki node_modules package-lock.json

Be sure to: chmod +x make-clean.sh

Suppressing an Annoyance:

See this for information on suppressing a superfluous unsaved-changes warning from your browser. The author says this is OK here.

Testing:

In addition to a smoke test of saving tiddlers and reloading the page to confirm they really got saved, confirm that the wiki is accessible via localhost:8080 and is not accessible at external-ip-address:8080.

Daemon:

Once you are satisfied that it works, you’ll want to set up LaunchCtl to launch it automagically.

Create a LaunchCtl file /Users/kevin/Library/LaunchAgents/com.kleinfelter.tiddlywiki.plist to specify how to run your container as a service:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.kleinfelter.tiddlywiki</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/kevin/Sync/Sites/tw-plain/runme.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/var/log/tiddlywiki/tiddlywiki.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/tiddlywiki/tiddlywiki.err</string>
</dict>
</plist>

Then:

sudo mkdir /var/log/tiddlywiki
sudo chown kevin /var/log/tiddlywiki

Then load your LaunchCtl with:

launchctl load /Users/kevin/Library/LaunchAgents/com.kleinfelter.tiddlywiki.plist

And check to be sure your service is working.

Customizing your wiki:

  • Control Panel:
    • Settings > Camel Case Wiki Links: Disable

I prefer a different look:

  • Go to http://j.d.material.tiddlyspot.com/
  • If you don’t get clear instructions, click the ‘hamburger menu’ to view the “Hello, world” page.
  • Follow the instructions on the page to install the theme and the ‘more material’ and the Roboto font, but NOT the swipe stuff.
    • Tip: Drag each icon from the source site into your TW, and then accept the import of all at once.
    • Tip: Download the roboto.tid and import it into your TW.
  • Go to the TW Control Panel, Appearance, Theme, and choose ‘Material’.
  • Note: IF you use FAB buttons, the secondary FABs only appear when you hover over the primary FAB.

Make My Own Palette:

  • Copy the Material palette into Material-KPK
  • It should have type=application/x-tiddler-dictionary
  • Tags=$:/tags/Palette
  • Fields:
    • name: Kevin’s Palette
    • description: Kevin’s Palette
  • Refresh, and it should show up with the other Palettes in Control Panel > Appearance.

Install Markdown:

  • There are two Markdown parsers available:
    • https://tiddlywiki.com/plugins/tiddlywiki/markdown - the official markdown plugin; supports PHP Extra dialect.
    • http://bjtools.tiddlyspot.com > MarKed - an unofficial markdown plugin; supports GFM dialect.
  • My standard Markdown dialect is GFM, and my Macbook tools are configured to support that. On the one hand, the official plugin is likely to be around as long as Tiddlywiki is, and there is some risk that BJttools could abandon his. OTOH, I’d like my tiddlers to use my standard Markdown dialect. That’s kinda key to my strategy of being able to access .tid files as if they were Markdown.
  • BJTools doesn’t include a new-Markdown button and the official plugin does. They use different content types, so I could actually install both. I’m going to install both and I’m going to tinker the official plugin’s button to create BJTools Markdown.
  • Install but do NOT use this markdown. It supports PHP Markdown Extra dialect. I have standardized on GFM (see my Macbook Configuration Management document.)
    • Drag and drop from https://tiddlywiki.com/plugins/tiddlywiki/markdown
    • Set dialect to Maruku by editing $:/config/markdown/dialect (shadow tiddler)
    • Adjust your Page toolbar to have the create-markdown button and NOT the create-wiki-markup button (via Control Panel > Appearance > Toolbars > Page Toolbar).
  • Install and do use this markdown. It supports GFM:
    • Drag and drop from http://bjtools.tiddlyspot.com > MarKed > $:/plugins/bj/plugins/marked
    • also import his Flexitype plugin.
  • Then edit $:/plugins/tiddlywiki/markdown/new-markdown-button (system tiddler) and change “text/x-markdown” (PHP) to “text/x-marked” (GFM)

Enable Selection and Close of View-mode Tiddlers:

Navigate to Sidebar > More > Shadows, and edit $:/core/ui/ViewTemplate. Replace it with the following:

\define frame-classes()
tc-tiddler-frame tc-tiddler-view-frame $(missingTiddlerClass)$ $(shadowTiddlerClass)$ $(systemTiddlerClass)$ $(tiddlerTagClasses)$
\end
\define folded-state()
$:/state/folded/$(currentTiddler)$
\end
<$set name="storyTiddler" value=<<currentTiddler>>><$set name="tiddlerInfoState" value=<<qualify "$:/state/popup/tiddler-info">>><$tiddler tiddler=<<currentTiddler>>><$keyboard key="alt-W" message="tm-close-tiddler"><div tabindex="1" class=<<frame-classes>>><$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate]!has[draft.of]]" variable="listItem"><$transclude tiddler=<<listItem>>/></$list>
</div>
</$keyboard>
</$tiddler></$set></$set>

For the record (and for some future release, when you have to derive this anew) the changes to the original ViewTemplate are:

  • Add to the div tag: tabindex=1
  • Wrap a keyboard widget around the div, with key=”alt-W” and message=”tm-close-tiddler”

Next:

  • Edit $:/config/Search/AutoFocus (shadow tiddler) and set it to: false

This implements a not-quite-perfect “Alt-W closes the current view-mode tiddler.” It does, in fact, close the currently selected tiddler. The gap is that you often must click on the tiddler to select it. I’d like to modify TW to auto-select a tiddler when it is opened/scrolled-to. I think this might be do-able (particularly in zoomin mode), but I haven’t figured it out yet. One possibility would be to edit $:/core/templates/tiddlywiki5.html and add an onFocus method to <body>. The trick there is that body might get the focus before the first tiddler is displayed. Maybe I need to add an onKeypress to tranfer focus or just redirect keystrokes from the body to the first tiddler. Or I could set a timer…

Set Up Tabbed Tiddlers:

  • Install StoryTabs from http://bjtools.tiddlyspot.com/ (drag the $ link for StoryTabs from his site to your TW.)
  • Go to Control Panel and set Appearance > Story View = zoomin
  • A note about StoryTabs: It solely adds the tabs above the story river. The reason it looks like tabbed tiddlers is because zoomin mode hides all tiddlers except for the ‘top’ tiddler in the river.

Hide the Story-date Line:

  • Browse to the very slow-to-load http://designwritestudio.updog.co
  • Drag this tiddler to your TW: $:/_Menu/Home/Configuration/Options
  • Rename it to “Configure Tiddler Subtitle”
  • Visit that tiddler and turn off display of subtitles and tags.

Enable Double-click to Edit:

  • Go to https://danielorodriguez.com/TW5-2click2edit/
  • Drag and drop the 2click2edit plugin into my wiki

Tiddlywiki With Docker On Macbook

Tiddlywiki on Node.js With Docker on My Macbook

CAUTION: I haven’t implemented this with live data yet. I decided it was too much change at once. (I’m a TW Classic user.) The staged implementation plan is:

  • Use the ‘Node.js’ TW natively (without Docker, without the MultiUser plugin). Soak. Get really comfortable with it.
  • Add MultiUser plugin. Soak. Get really comfortable with it.
  • Wrap it in Docker.

That way, if there is weirdness, I’ll recognize which piece of the puzzle to blame.

That being said, here’s how my proof-of-concept project went…

Introduction:

I decided to run the Node.js edition of Tiddlywiki on my Macbook. I elected to use Docker so that all dependencies can be contained (i.e. in a container). That way, if I later decide to run a different version of Node.js for another project, Tiddlywiki’s Node.js remains unchanged. And I also threw in the MultiUser plugin. (I don’t care about multi-user. It also adds sub-wikis served from the same port.)

This document focuses on the Tiddlywiki and Node.js aspects. My general notes about Dockerfile and docker-compose are here.

Documentation for the plugin is here: https://github.com/OokTech/TW5-MultiUser


First, define some constants in .bash_profile, to reduce redundant typing:

export twd='/Users/kevin/Sync/Sites/tw-node'
alias twd="cd $twd"

Then

. ~/.bash_profile 
mkdir $twd
cd $twd

Some explanation about my approach:

I wanted to put all the setup in the Dockerfile, and it worked pretty well that way until I added MultiUser plugin. Without the plugin, you can put your wiki files anywhere you want. With the plugin, it wants your wiki(s) to live under node_modules/tiddlywiki/editions. If you want a persistent wiki, you have to mount your wiki directory on a host directory, but you can’t mount onto a host directory from the Dockerfile (because they want that directory to be flexible at runtime). You can’t update node_modules/tiddlywiki/editions at setup (from the Dockerfile) and also mount it at runtime.

So I do a lot of conditional ‘setup’ in the container-rc.sh script.

Continuing:

Create ‘Dockerfile’ containing:

# Use the node image, based on the alpine micro-Linux image as our starting point.
FROM node:alpine
RUN apk add --no-cache git

# Define a volume for mounting on host file system.  (i.e. Enable mounting this path.)
VOLUME /var/lib/tiddlywiki

# set the container's working directory for any 
# RUN, CMD, ENTRYPOINT, COPY and ADD commands *below*.
WORKDIR /var/lib/tiddlywiki

# Copy container-rc.sh from the Dockerfile directory into the image.
# Add a container startup script (runs inside the container.  e.g. AUTOEXEC.BAT)
ADD container-rc.sh /usr/local/bin/container-rc.sh

# These are the VM port.  At run-time, map them to host IP:ports
# We need two consecutive ports: One for the wiki server and the MultiUser plugin
# uses another port for its 'websockets'
EXPOSE 8080 8081

Create docker-compose.yml containing (reminder - no tabs in YAML):

version: "3"                    # Using version 3 docker-compose file format.
services:
    tw-node:                    # Name of the image? service? I'm creating.
        build: .                # Use the Dockerfile in . to build the image
        ports:
                - "127.0.0.1:8080:8080" # host:container.
                - "127.0.0.1:8081:8081" # host:container.
                                # Could also use "8080:8080" to not specify host IP.
        # volume mount syntax= /host/path:/container/path
        volumes:
           - /Users/kevin/Sync/Sites/tw-node:/var/lib/tiddlywiki
        command: /bin/sh /usr/local/bin/container-rc.sh

Build the image:

docker-compose build --no-cache

Create runme.sh containing:

#!/bin/sh
docker-compose up

Create container-rc.sh containing:

#!/bin/sh
# I have to do much of the setup in here because I want to put my files onto the mounted volume

# Abort immediately on shell errors
set -e

TWD=/var/lib/tiddlywiki
cd $TWD

# This is how you would initialize a non-plugin wiki
#if [ ! -d /var/lib/tiddlywiki/mywiki ]; then
#  /usr/local/bin/node /usr/local/bin/tiddlywiki mywiki --init server
#fi

if [ ! -d node_modules/tiddlywiki/editions/MultiUserWiki ]; then

  echo "Installing tiddlywiki node package locally. This will take several minutes..."
  npm install tiddlywiki@5.1.15

  cd $TWD/node_modules
  echo "CLONING MultiUser plugin"
  git clone --depth=1 https://github.com/OokTech/TW5-MultiUser.git tiddlywiki/plugins/OokTech/MultiUser

  echo "Copying starter wiki"
  cp -r tiddlywiki/plugins/OokTech/MultiUser/MultiUserWiki tiddlywiki/editions/
fi

cd $TWD/node_modules/tiddlywiki

echo "Launching node"
exec /usr/local/bin/node ./tiddlywiki.js editions/MultiUserWiki  --wsserver 8080 ${USERNAME:-user} ${PASSWORD:-'wiki'} 0.0.0.0

Note that this uses the latest edition of the MultiUser plugin. If you want to specify a version you’ll need to:

  • Clone the plugin repository to something like /tmp/MultiUser.
  • cd to that directory and run git log. Identify the commit hash of the version you want.
  • git clone commithash-here

Be sure to: chmod +x runme.sh and chmod +x container-rc.sh

Launch your wiki with:

./runme.sh

Note that if you want to start from a clean slate (i.e. re-run the init stuff), you’ll find it handy to have a script. Create a file named make-clean.sh:

#!/bin/bash
echo "Run this only if you want to delete all the wiki content and start over. Press control-C to stop or Enter to continue."
read
rm -rf /tmp/node_modules
rm -rf /tmp/package-lock.json

mv node_modules /tmp
mv package-lock.json /tmp

Be sure to: chmod +x make-clean.sh

You need not re-build the image after a make-clean… just start the container and it will re-run the init.

Suppressing an Annoyance:

See this for information on suppressing a superfluous unsaved-changes warning from your browser. The author says this is OK here.

A Real Problem:

When running under Docker, if I create a title-only tiddler (no body), it looks like it saved it (i.e. the red save button goes gray), but it doesn’t show up under the ‘Recent’ tab and it is unable to display it when the page is reloaded. You can make them show up by (in the host) navigating to that wiki’s data folder and running touch * (but that’s gonna set the time on every tiddler).

Plain Node.js TW doesn’t have this problem in a container. The plugin does not have this problem when run outside a container.

The plugin’s author says the tiddler is visible to the wiki - it just isn’t listed under Recents. I think he’s right. He says it’s a known bug.

Testing:

In addition to a smoke test of saving tiddlers (in the sub-wikis) and reloading the page to confirm they really got saved, confirm that the wiki is accessible via localhost:8080 and is not accessible at external-ip-address:8080.

Daemon:

Once you are satisfied that it works, you’ll want to set up LaunchCtl to launch it automagically.

Create a LaunchCtl file /Users/kevin/Library/LaunchAgents/com.kleinfelter.tw-node.plist to specify how to run your container as a service:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.kleinfelter.tw-node</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/kevin/Sync/Sites/tw-node/runme.sh</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/var/log/tw-node.log</string>
    <key>StandardErrorPath</key>
    <string>/var/log/tw-node/tw-node.err</string>
</dict>
</plist>

Then:

sudo mkdir /var/log/tw-node
sudo chown kevin /var/log/tw-node

Then load your LaunchCtl with:

launchctl load /Users/kevin/Library/LaunchAgents/com.kleinfelter.tw-node.plist

And check to be sure your service is working.

Kevins Guide To Using Dockerfile

Kevin’s Guide to Using Dockerfile and docker-compose.yml

and generaly, how to docker-enable an application.

As a rule, don’t install libraries or scripting languages directly onto your host. e.g. The moment you install Ruby 1.9.1 onto your host, you’ll run into a must-have app which requires Ruby 1.8.5. Yes, there are ways to manage multiple Ruby (or multiple Python or multiple libc), but it is just safer and cleaner to put all that stuff inside a container.

A Dockerfile specifies how to build an image. It doesn’t address how to run that image as a container. docker-compose is a tool to run your images. Here’s how I use something new under Docker on my Mac.

Strictly speaking, docker-compose is about running multiple containers. However, you can use it to run a single container, and it makes the command line for that container simpler, by allowing you to put some of your options in the docker-compose file.

Note: This isn’t quite how I set up my Jekyll images. Those were done before I defined my canonical form.

  1. Create a folder for your app. We’ll refer to it as app-dir. Put everything shown below into this folder.

  2. Write the Dockerfile just to build the image, not to run it. e.g.

    # Use the specified image as a baseline.
    # See https://hub.docker.com/explore/ for official images.
    # Format is one of:
    #   FROM image
    #   FROM image:tag
    #   FROM image
    # The one below comes directly from the doc at https://hub.docker.com/_/node/
    FROM node:alpine
    
    # The RUN command says, "After you download the image, run this command to
    # customize the image to your needs.  Runs as part of building (not starting) the image
    #
    # The RUN below says:
    #   Install v 5.1.15 of Tiddlywiki (TW) into Node.js using npm (the Node package mgr).  #   You could specify a newer version (or no version, by omitting '@5.1.15') but you 
    #  may have to adapt these instructions if TW has major changes.
    # I recommend that you ALWAYS specify a version or a tag.
    RUN npm install -g tiddlywiki@5.1.15
    RUN /usr/local/bin/node /usr/local/bin/tiddlywiki wiki-data --init server
    
    # Define a volume for mounting on host file system.  (i.e. Enable mounting this path.)
    VOLUME /var/lib/tiddlywiki
    
    # set the container's working directory for any 
    # RUN, CMD, ENTRYPOINT, COPY and ADD commands *below*.
    # Reminder: TW writes its files to the working directory.
    WORKDIR /var/lib/tiddlywiki
    
    # Add a script which you will (later) launch inside the container to do tasks
    # you want done EACH time the container starts.
    ADD container-rc.sh /usr/local/bin/container-rc.sh
    
    # Expose any ports.  Note that on Win/Mac, Docker runs in a VM and this is the
    # VM port.  You need to map the VM port to a host port at runtime, if you are to
    # access it.
    EXPOSE 8080
    

  3. Write docker-compose.yml to specify how to run the image. (Reminder: No tabs in YAML.)

    version: "3"                    # Using version 3 docker-compose file format.
    services:
        tw-node:                    # Name of the image? service? I'm creating.
            build: .                # Use the Dockerfile in . to build the image
            ports:
                - "127.0.0.1:8080:8080"
                                    # host:container.  Quotes strongly recommended.
                                    # Two forms: hostPort:containerPort or 
                                    #     hostIP:hostPort:containerPort
            volumes:
                                    # /host/path:/container/path
                - /Users/kevin/Sync/Sites/tw-node:/var/lib/tiddlywiki
            command: /bin/sh /usr/local/bin/container-rc.sh
    
  4. Create runme.sh, to launch the container, containing

    #!/bin/bash
    cd /path/to/app-dir
    /usr/local/bin/docker-compose up
    
  5. Create container-rc.sh, which starts the processes inside the container:

    ??
    
  6. Be sure to:

    chmod +x runme.sh
    chmod +x container-rc.sh
    

  7. Build your image with:

    docker-compose build --no-cache
    
    • You can usually omit –no-cache, and it will use cached layers upon which this image depends. Adding –no-cache ensures that you don’t get a layer you cached which was built with special args. I don’t do a lot of building, so I don’t mind if it is slow.
  8. Test your image with:

    ./runme.sh
    
  9. It can take 15-20 seconds to start a simple image. You’ll usually see this when it is up: “Server running… press ctrl-c to stop.”

  10. Once you validate your service is working, press control-C to stop it.

  11. Create a LaunchCtl file /Users/kevin/Library/LaunchAgents/com.kleinfelter.SERVICE_NAME_HERE.plist to specify how to run your container as a service:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
        <key>Label</key>
        <string>com.kleinfelter.SERVICE_NAME_HERE</string>
        <key>ProgramArguments</key>
        <array>
            <string>/bin/bash</string>
            <string>/path/to/app-dir/runme.sh</string>
        </array>
        <key>RunAtLoad</key>
        <true/>
        <key>KeepAlive</key>
        <true/>
        <key>StandardOutPath</key>
        <string>/var/log/SERVICE_NAME_HERE/SERVICE_NAME_HERE.log</string>
        <key>StandardErrorPath</key>
        <string>/var/log/SERVICE_NAME_HERE/SERVICE_NAME_HERE.err</string>
    </dict>
    </plist>
    
  12. sudo mkdir /var/log/SERVICE_NAME_HERE
    sudo chown kevin /var/log/SERVICE_NAME_HERE
    
  13. Then load your LaunchCtl with:

    launchctl load /Users/kevin/Library/LaunchAgents/com.kleinfelter.SERVICE_NAME_HERE.plist
    
  14. And check to be sure your service is working.

  15. Reminder: To open a bash prompt in your image (without installing ssh and using ssh):

    docker docker ps --format 'table ' #Identify the desired container name
    docker exec -it CONTAINER_NAME_HERE /bin/bash #-i gets STDIN, -t gets a pseudo-tty
    
  16. Rejoice!