Showing posts with label bash. Show all posts
Showing posts with label bash. Show all posts

Friday, September 12, 2014

Auto-Update of Dart on Linux


Updating Dart packages as part of an automated test suite is easy (pub upgrade). But what about updating Dart itself?

As a coder, my instinct is a curl script to check the dartlang.org page for the latest release, then a Bash script to compare that value with the current install, then download, unzip and install. I am almost tempted to head down that rabbit hole—like all rabbit holes, it sounds fun from the outside. But, <deep breath>, maybe there is a better way.

The DartEditor has an auto-update feature, but if that is easily accessed from the command-line or code, I cannot find it. Shame. That too sounds like a fun rabbit hole.

So instead, I note that there are Debian packages for the SDK. I could add my CI server's user to the sudoers list for updating the package list and upgrading the SDK. I will not easily be able to trigger a new build when new versions of Dart are available, but I probably want to run the test suite every night regardless. Also, I like my grapes sour, dammit.

But that won't work either. My CI server is still 32-bit. Bleh.

So rabbit hole #1 it is. I will write a Bash script that will compare the currently installed version of Dart with the latest—installing the latest as needed.

First up, determining the latest version. Instead of scraping the dartlang.org site, I find that the VERSION resource at https://storage.googleapis.com/dart-archive/channels/stable/release/latest/VERSION is a better bet:
$ curl https://storage.googleapis.com/dart-archive/channels/stable/release/latest/VERSION 2>/dev/null                      ~/Downloads
{
  "revision": "39553",
  "version" : "1.6.0",
  "date"    : "201408270039"
}
Thanks to curl, I can grab that JSON and run it through grep and sed to find what I need:
VERSION_URL='https://storage.googleapis.com/dart-archive/channels/stable/release/latest/VERSION'

latest=`curl $VERSION_URL 2>/dev/null |
  grep version |
  sed -e 's/\s*"version"\s*:\s*"//' \
      -e 's/".*//'`
Checking the currently installed version is easy enough, though I do set +e to prevent the script from halting if the dart executable is not already installed:
set +e
current=`drt --version 2>&1 |
  sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/'`
set -e
I am fairly proficient at what comes next. I remove the “old” Dart (a backup of the currently installed Dart), then move the currently installed Dart to dart.old. Last, I download and unzip Dart:
rm -rf $ROOT_DIR/dart.old
mv $ROOT_DIR/dart $ROOT_DIR/dart.old

rm -f /tmp/dart.zip
wget -O /tmp/dart.zip $DOWNLOAD_URL

cd $ROOT_DIR
unzip /tmp/dart.zip
I am using the DartEditor as the DOWNLOAD_URL as I need the content_shell associated with it for testing. The DartEditor zip extracts into the dart directory so I have my replacement for the old $ROOT_DIR/dart install.

Last, I need to download content_shell for testing. The DartEditor zip includes a download script, but not the actual executable as it is somewhat largish. This should do the trick:
cd $ROOT_DIR/dart

./chromium/download_contentshell.sh
unzip content_shell-linux-*-release.zip
mv drt-lucid*-full-stable-* content_shell
The whole script is then:
#!/bin/sh

ROOT_DIR='/home/chris/local'

VERSION_URL='https://storage.googleapis.com/dart-archive/channels/stable/release/latest/VERSION'
DOWNLOAD_URL='https://storage.googleapis.com/dart-archive/channels/stable/release/latest/editor/darteditor-linux-ia32.zip'

latest=`curl $VERSION_URL 2>/dev/null |
  grep version |
  sed -e 's/\s*"version"\s*:\s*"//' \
      -e 's/".*//'`

set +e
current=`dart --version 2>&1 |
  sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/'`
set -e

echo "Latest: $latest"
echo "Current: $current"
echo ""

if [ "$current" = "$latest" ]; then
    echo "Up to date."
    exit 0
fi

echo "Downloading the latest version of Dart!"
echo ""

rm -rf $ROOT_DIR/dart.old
set +e
mv $ROOT_DIR/dart $ROOT_DIR/dart.old
set -e

rm -f /tmp/dart.zip
wget -nv -O /tmp/dart.zip $DOWNLOAD_URL

cd $ROOT_DIR
unzip -q /tmp/dart.zip

cd $ROOT_DIR/dart

./chromium/download_contentshell.sh
unzip content_shell-linux-*-release.zip
mv drt-lucid*-full-stable-* content_shell
Which does the trick. Even on my Debian 32 bit CI server:



Sometime the first rabbit hole really is the best option.


Day #181

Wednesday, September 3, 2014

Debugging Transient Test Failures with Bash One Liners


I confess that I am rather pleased with myself. I almost certainly messed something up, so it's not like I am going to get a big head or anything. But, for the moment, I am pleased with myself.

The new eee-polymer-tests generator for Karma / Jasmine testing of Polymer elements works pretty darn well. Even if I start a Polymer project with no tests:
$ rm -rf node_modules karma.conf.js test
Making the project dependent on eee-polymer-tests in an NPM package.json:
{
  "name": "parent-events",
  "devDependencies": {
    "eee-polymer-tests": "eee-c/eee-polymer-tests"
  }
}
Will install Karma, Jasmine and generate tests:
$ npm install
|
> eee-polymer-tests@0.0.1 postinstall /home/chris/repos/polymer-book/book/code-js/parent_events/node_modules/eee-polymer-tests
> ./generator.js

What is the name of the Polymer element being tested? click-sink

Generating test setup for: click-sink
Done!
Even better, the generated test, albeit quite simple, actually works without making any changes:
karma start --single-run
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket ciIVgf_I_GJhvyvorEtq with id 85510144
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.049 secs / 0.043 secs)
Well… it works most of the time. Every now and then, I get a failure like:
karma start --single-run
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket -cnZn5zrZF_OLuYjrF2l with id 31182079
Chrome 37.0.2062 (Linux) ERROR
  Uncaught TypeError: Cannot read property 'register' of undefined
  at /home/chris/repos/polymer-book/book/code-js/parent_events/bower_components/platform/platform.js:15
Oh, how I hate transient errors like this.

I get a failure like the one above roughly one out of every fives runs. To be sure that I have solved the problem, I am probably going to have to run the test file 10 times before I have any confidence. Fortunately, there are Bash one-liners for just this kind of thing:
$ for i in {1..10}; do karma start --single-run; done
Interestingly—and hopefully something of a clue—this passes for my hand-written specs (all two tests of them):
$ for i in {1..10}; do karma start --single-run; done
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.083 secs / 0.075 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.071 secs / 0.065 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.071 secs / 0.066 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.073 secs / 0.066 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.076 secs / 0.071 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.076 secs / 0.073 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.088 secs / 0.08 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.062 secs / 0.055 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.068 secs / 0.063 secs)
Chrome 37.0.2062 (Linux): Executed 2 of 2 SUCCESS (0.066 secs / 0.062 secs)
Perhaps this has something to do with the number of tests? To test that theory, I comment out one test and re-run the remaining test 10 times:
$ for i in {1..10}; do karma start --single-run; done
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.062 secs / 0.056 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.059 secs / 0.054 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.053 secs / 0.049 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.058 secs / 0.052 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.057 secs / 0.051 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.055 secs / 0.049 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.055 secs / 0.05 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.05 secs / 0.045 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.054 secs / 0.05 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.061 secs / 0.055 secs)
Nope. That's not it.

This suggests that either the problem is in the contents of my tests (the generated one is simpler than my actual hand-coded one that currently seems to work) or the problem has to do with the size of my test file (I only commented out the one test). It seems easier to delete the commented out test in my current spec as a next step. After doing so, I find:
$ for i in {1..10}; do karma start --single-run; done
Chrome 37.0.2062 (Linux) ERROR
  Uncaught TypeError: Cannot read property 'register' of undefined
  at /home/chris/repos/polymer-book/book/code-js/parent_events/bower_components/platform/platform.js:15

Chrome 37.0.2062 (Linux) ERROR
  Uncaught TypeError: Cannot read property 'resolveDom' of undefined
  at /home/chris/repos/polymer-book/book/code-js/parent_events/bower_components/polymer/polymer.js:14

Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.056 secs / 0.051 secs)
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.055 secs / 0.05 secs)
...
Bingo.

The most obvious solution—and possibly the one with which I am stuck—is to jam a bunch of comments in there. Heck I could even call it documentation. But why is this happening?

Update: Through process of elimination, I track this down to PolymerSetup.js, which does its level best to simulate a normal Polymer setup on a web page:
// 1. Load Polymer before any code that touches the DOM.
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

// 2. Load component(s)
var link = document.createElement("link");
link.rel = "import";
link.href = "/base/elements/click-sink.html";
document.getElementsByTagName("head")[0].appendChild(link);
The Polymer documentation is adamant that platform.js be the first script on a page—and this is probably why. And, even though it is the first script here and even though PolymerSetup.js is the first script in the Karma files list and even though it is the first file loaded (verified with --log-level debug on Karma), this is still not always the first file evaluated. And I think that is the cause. If my spec file is sufficiently small, it will be loaded and evaluated before this setup file is able to add platform.js in its proper place.

As I see it, I have three options:
  1. Move code that waits for polymer-ready out of PolymerSetup.js into the spec file (making the spec file large enough so that PolymerSetup.js loads and is evaluated first.
  2. Remove the platform.js inclusion from PolymerSetup.js and add it first to the files list in karma.conf.js
  3. Add junk data to be loaded before the spec files.
Crazy as it might seem, I may opt for #3. I have verified (via Bash for-loops) that all three options work. But the last one is probably the best Polymer solution.

The code that waits for polymer-ready feels like PolymerSetup.js so I would like to keep it there. The proper Karma solution would be #2, but I would end up with platform.js and the element being loaded from two different locations—and via two different mechanisms. The current PolymerSetup.js is not perfect, but it is as close to a normal Polymer solution as I can get (Karma makes it very hard to override the static page that holds the tests).

So adding a dummy file, with an explanation at the top, seems the best bet for my purposes.




Day #172

Tuesday, August 26, 2014

Git Subcommand to Sort by Last Modified Date


OK, it's time to get serious about updating Patterns in Polymer and finishing the screencasts. But where to start? I know what needs to be done for the screencasts: record them (the upcoming long US weekend should help there). But how best to focus my efforts on the chapters in the book?

Working through each chapter based on last modified date seems the best bet, but all of the chapter files on my filesystem have the same last modified timestamp:
$ ls -l *asc
...
-rw-r--r-- 1 chris chris 13163 Jun 28 10:06 i18n.asc
-rw-r--r-- 1 chris chris  5213 Jun 28 10:06 live_reload.asc
-rw-r--r-- 1 chris chris 10713 Jun 28 10:06 mdv.asc
-rw-r--r-- 1 chris chris  7917 Jun 28 10:06 plain_old_forms.asc
-rw-r--r-- 1 chris chris 12495 Jun 28 10:06 polymer.asc
-rw-r--r-- 1 chris chris   794 Jun 28 10:06 strategy.asc
Either I made a very large commit on June 28 at 10:06 or that was the last time that I checked these files out fresh (either from a new clone or switching from a sparse branch).

In fact, the last time that I updated the i18n chapter was back in April (yikes!):
$ git log i18n.asc | head -5
commit 030968dff38794ba7e20fa3ae839fc62a999eee2
Author: Chris Strom 
Date:   Sun Apr 20 23:39:09 2014 -0400

    Fix up i18n copy and code
So how do I go about sorting by the last-modified-in-git date? That turns out to be tricky. Thankfully, there is a nice Stack Overflow post on the subject.

The date timestamp from that article will not help sorting, but ISO 8601 will. Happily, git-log supports logging with ISO 8601:
$ git log -1 --date=iso --format="%ad" i18n.asc 
2014-04-20 23:39:09 -0400
It have the feeling that I would like to use this again, so I will create a git subcommand: git-lslt (similar to the usual ls -lt). Git subcommands are trivial—I need only a script prefixed with git-. So I create $HOME/bin/git-lslt as:
#!/bin/sh

ls | \
  while read filename; do
    echo "$(git log -1 --date=iso --format="%ad" -- $filename) $filename"
  done | \
  grep '^2' | \
  sort
Most of that is from the Stack Overflow article. The sort is self-explanatory. The grep '^2' selects only entries with date stamps—meaning that files not tracked by git will be ignored.

The output looks like:
$ git lslt
2013-11-16 16:26:38 -0500 about.asc
2013-11-16 16:26:38 -0500 chapter_1.asc
2013-11-16 17:51:48 -0500 getting_help.asc
2014-01-14 19:58:38 -0500 book_dart.asc
2014-01-14 19:58:38 -0500 book_js.asc
2014-02-12 02:52:49 -0500 copyright.asc
...
Most of that is boilerplate code that never needs an update. I suspect that the "about" file has since been absorbed into the introduction and can be deleted.

After grepping the output a bit more, I find a few surprises:
$ git lslt | \
   grep .asc | \
   grep -v 'chapter_1\|contents\|copyright\|book.*.asc\|bibliography'
...
2014-02-14 01:13:27 -0500 parent_events.asc
2014-02-15 01:16:04 -0500 parent_child.asc
2014-02-15 01:16:04 -0500 svg.asc
2014-02-15 16:12:16 -0500 configuration.asc
...
I am surprised that the parent events and parent-child chapters have not been updated. I could have sworn that I got (and incorporated) feedback on at least one of those. The configuration chapter has since been removed (possibly permanently), so that is no surprise. But I have learned a ton about SVG since February—I am quite surprised that I have not included any of that knowledge in that chapter.

My new git lslt subcommand has already paid some nice dividends. I know where I need to focus my initial update/rewrite efforts. Starting tomorrow.


Day #164

Monday, July 21, 2014

Refactoring Bash Scripts


I'll be honest here: I'm a pretty terribly Bash script coder. I find the man page too overwhelming to really get better at it. If I need to do something in Bash—even something simple like conditional statements—I grep through /etc/init.d scripts or fall back to the Google machine.

But tonight, there is no hiding. I have two Bash scripts (actually, just shell scripts at this point) that do nearly the same thing: benchmark.sh and benchmark_js.sh. Both perform a series of benchmarking runs of code for Design Patterns in Dart, the idea being that it might be useful to have actual numbers to back up some of the approaches that I include in the book. But, since this is Dart, it makes sense to benchmark both on the Dart VM and on a JavaScript VM (the latter because most Dart code will be compiled with dart2js). The two benchmark shell scripts are therefore responsible for running and generating summary results for Dart and JavaScript.

The problem with two scripts is twofold. First, I need to keep them in sync—any change made to one needs to go into the other. Second, if I want to generalize this for any design pattern, I have hard-coded way too much in both scripts. To the refactoring machine, Robin!

To get an idea where to start, I diff the two scripts. I have been working fairly hard to keep the two scripts in sync, so there are only two places that differ. The JavaScript version includes a section that compiles the Dart benchmarks in to JavaScript:
$ diff -u1 tool/benchmark.sh tool/benchmark_js.sh 
--- tool/benchmark.sh   2014-07-21 22:32:44.047778498 -0400
+++ tool/benchmark_js.sh        2014-07-21 20:54:20.803634500 -0400
@@ -11,2 +11,16 @@
 
+# Compile
+wrapper='function dartMainRunner(main, args) { main(process.argv.slice(2)); }';
+dart2js -o tool/benchmark.dart.js \
+           tool/benchmark.dart
+echo $wrapper >> tool/benchmark.dart.js
+
+dart2js -o tool/benchmark_single_dispatch_iteration.dart.js \
+           tool/benchmark_single_dispatch_iteration.dart
+echo $wrapper >> tool/benchmark_single_dispatch_iteration.dart.js
+
+dart2js -o tool/benchmark_visitor_traverse.dart.js \
+           tool/benchmark_visitor_traverse.dart
+echo $wrapper >> tool/benchmark_visitor_traverse.dart.js
+
...
The other difference is actually running the benchmarks—the JavaScript version needs to run through node.js:
$ diff -u1 tool/benchmark.sh tool/benchmark_js.sh 
--- tool/benchmark.sh   2014-07-21 22:32:44.047778498 -0400
+++ tool/benchmark_js.sh        2014-07-21 20:54:20.803634500 -0400
...
@@ -15,7 +29,7 @@
 do
-    ./tool/benchmark.dart --loop-size=$X \
+    node ./tool/benchmark.dart.js --loop-size=$X \
         | tee -a $RESULTS_FILE
-    ./tool/benchmark_single_dispatch_iteration.dart --loop-size=$X \
+    node ./tool/benchmark_single_dispatch_iteration.dart.js --loop-size=$X \
         | tee -a $RESULTS_FILE
-    ./tool/benchmark_visitor_traverse.dart --loop-size=$X \
+    node ./tool/benchmark_visitor_traverse.dart.js --loop-size=$X \
         | tee -a $RESULTS_FILE
For refactoring purposes, I start with the latter difference. The former is a specialization that can be performed in a single conditional. The latter involves both uses of the script.

That will suffice for initial strategy, what about tactics? Glancing at the Dart benchmark.sh script, I see that I have a structure that looks like:
#!/bin/bash

RESULTS_FILE=tmp/benchmark_loop_runs.tsv
SUMMARY_FILE=tmp/benchmark_summary.tsv
LOOP_SIZES="10 100 1000 10000 100000"

# Initialize artifact directory
...

# Individual benchmark runs of different implementations
...

# Summarize results
...

# Visualization ready
...
Apparently I have been quite fastidious about commenting the code because those code section comments are actually there. They look like a nice first pass a series of functions. Also of note here is that the script starts by setting some global settings, which seems like a good idea even after refactoring—I can use these and others to specify output filenames, benchmark scripts, and whether or not to use the JavaScript VM.

But first things first, extracting the code in each of those comment sections out into functions. I make a top-level function that will invoke all four functions-from-comment-sections:
_run_benchmarks () {
    initialize
    run_benchmarks
    summarize
    all_done
}
Then I create each function as:
# Initialize artifact directory
initialize () {
  # ...
}

# Individual benchmark runs of different implementations
run_benchmarks () {
  # ...
}

# Summarize results
summarize () {
  # ...
}

# Visualization ready
all_done () {
  # ...
}
Since each of those sections is relying on top-level global variables, this just works™ without any additional work from me.

Now for some actual refactoring. One of the goals here is to be able to use this same script not only for JavaScript and Dart benchmarking of the same pattern, but also for different patterns. To be able to use this for different patterns, I need to stop hard-coding the scripts inside the new run_benchmarks function:
run_benchmarks () {
    echo "Running benchmarks..."
    for X in 10 100 # 1000 10000 100000
    do
      ./tool/benchmark.dart --loop-size=$X \
          | tee -a $RESULTS_FILE
      ./tool/benchmark_single_dispatch_iteration.dart --loop-size=$X \
          | tee -a $RESULTS_FILE
      ./tool/benchmark_visitor_traverse.dart --loop-size=$X \
          | tee -a $RESULTS_FILE
    done
    echo "Done. Results stored in $results_file."
}
The only thing that is different between those three implementation benchmarks is the name of the benchmark file. So a list of files in a global variable that could be looped over is my next step:
BENCHMARK_SCRIPTS=(
    tool/benchmark.dart
    tool/benchmark_single_dispatch_iteration.dart
    tool/benchmark_visitor_traverse.dart
)
Up until this point, I think I could get away with regular Bourne shell scripting, but lists like this are only available in Bash. With that, I can change run_benchmarks to:
run_benchmarks () {
    echo "Running benchmarks..."
    for X in 10 100 1000 10000 100000
    do
        for script in ${BENCHMARK_SCRIPTS[*]}
        do
            ./$script --loop-size=$X | tee -a $results_file
        done
    done
    echo "Done. Results stored in $results_file."
}
At this point, I would like to get a feel for what the common part of the script is and what specialized changes are needed for each new pattern benchmark. So I move all of my new functions out into a _benchmark.sh script that can be ”sourced” by the specialized code:
#!/bin/bash

source ./tool/_benchmark.sh

BENCHMARK_SCRIPTS=(
    tool/benchmark.dart
    tool/benchmark_single_dispatch_iteration.dart
    tool/benchmark_visitor_traverse.dart
)

RESULTS_FILE=benchmark_loop_runs.tsv
SUMMARY_FILE=benchmark_summary.tsv

_run_benchmarks
That is pretty nice. I can easily see how I would use this for other patterns—for each implementation being benchmarked, I would simple add them to the list of BENCHMARK_SCRIPTS.

Now that I see what is left of the code, I realize that the RESULTS_FILE and SUMMARY_FILE variables were only varied to keep from overwriting artifacts of the different VM runs. The basename for each remains the same between the two original scripts—the JavaScript artifacts include an additional _js. That is the kind of thing that can be moved into my new common _benchmark.sh file:
#...
results_basename="benchmark_loop_runs"
summary_basename="benchmark_summary"
results_file=""
summary_file=""
type="js"
# ...
initialize () {
    if [ "$type" = "js" ]; then
        results_file=$tmpdir/${results_basename}_js.tsv
        summary_file=$tmpdir/${summary_basename}_js.tsv
    else
        results_file=$tmpdir/${results_basename}.tsv
        summary_file=$tmpdir/${summary_basename}.tsv
    fi
    # ...
}
If _run_benchmarks is invoked when type is set to "js", then the value of the results_file variable will then include _js in the basename. If "js" is not supplied, then the old basename will be used.

To obtain that information from the command-line, I read the first argument supplied when the script is called ($1) and send it to _run_benchmarks:
_run_benchmarks $1
I can then add a new (very simple) parse_options function to see if this script is running the Dart or JavaScript benchmarks:
_run_benchmarks () {
    parse_options $1
    initialize
    run_benchmarks
    summarize
    all_done
}

parse_options () {
  if [ "$1" = "-js" -o "$1" = "--javascript" ]
  then
      type="js"
  fi
}
# ...
The $1 inside _run_benchmarks is not the same as the $1 from the command-line. Inside a function, $1 refers to the first argument supplied to it.

I can also make use of this to pull in the last piece of refactoring—the compiling that I decided to save until later. Well, now it is later and I can compile as needed when type has been parsed from the command line to be set to "js":
_run_benchmarks () {
    parse_options $1
    initialize
    if [ "$type" = "dart" ]; then
        run_benchmarks
    else
        compile
        run_benchmarks_js
    fi
    summarize
    all_done
}
With that, I have everything I need to keep the specialized versions of the benchmarking script small:
#!/bin/bash

source ./tool/_benchmark.sh

BENCHMARK_SCRIPTS=(
    tool/benchmark.dart
    tool/benchmark_single_dispatch_iteration.dart
    tool/benchmark_visitor_traverse.dart
)

_run_benchmarks $1
No doubt there is much upon which I could improve if I wanted to get really proficient at Bash scripting, but I am reasonably happy with this. It ought to suit me nicely when I switch to other patterns. Which I will try next. Tomorrow.



Day #129

Monday, May 5, 2014

Query is Not Deprecated in Dart


Look, I know that Dart Editor says that query() is deprecated in Dart, but it's really not.

I mean, sure it's deprecated, but it's deprecated so that a DOM method of the same name that returns nearly the same thing can replace it. And, since the newer version will (most likely) return an active node list, it'll be more what developers tend to expect and use less memory than the current version. So I stick with query() in my various books and will continue to do so until the deprecation is completed and the new an improved query() arrives.

That leaves me with a bit of a bother, however. What is the best practice for ignoring query() / queryAll() “deprecation” warnings? In Dart for Hipsters, I had been ignoring all hints in my test_runner.sh:
# Static type analysis
echo
echo "Static type analysis (expecting 4 warnings)..."

results=$(dartanalyzer --no-hints test.dart 2>&1)
I think that is probably a case of throwing the baby out with the bath water. So instead, I opt for an approach that we have found works nicely in the ICE Code Editor test suite—we grep-ignore hints that are OK which should leave me with no hints. In the book's test quite, this looks something like:
echo "Static type analysis..."

results=$(dartanalyzer test.dart 2>&1)
results_ignoring_ok_deprecations=$(
  echo "$results" | \
    grep -v "'query' is deprecated" | \
    grep -v "'queryAll' is deprecated" | \
    grep -v "hints found.$"
)
echo "$results_ignoring_ok_deprecations"
The results variable name, results_ignoring_ok_deprecations is a bit, but it was the best that we could come up with—and all agreed that a long, descriptive name was better than the alternative.

Since I am intentionally writing some broken code in Dart for Hipsters, I also use this as an opportunity to ignore warnings in specific files from specific lines. The static dartanalyzer code thus becomes:
echo "Static type analysis..."

results=$(dartanalyzer test.dart 2>&1)
results_ignoring_ok_deprecations=$(
  echo "$results" | \
    grep -v "'query' is deprecated" | \
    grep -v "'queryAll' is deprecated" | \

    # Known / intentional errors:
    grep -v "code/primitives/types.dart, line 34" | \
    grep -v "code/primitives/types.dart, line 42" | \
    grep -v "code/varying_the_behavior/test/calling_methods_from_no_such_method.dart, line 22" | \
    grep -v "code/varying_the_behavior/test/calling_methods_from_no_such_method.dart, line 26" | \

    grep -v "hints found.$"
)
echo "$results_ignoring_ok_deprecations"


count=$(echo "$results_ignoring_ok_deprecations" | wc -l)
if [[ "$count" != "1" ]]
then
  exit 1
fi

echo
echo "Looks good!"
I feel much better about that approach than the previous baby and bath water approach. Now, if you'll excuse me, it seems that I have two dozen or so unused imports in my code...


Day #55

Thursday, August 29, 2013

Maybe Bash is Better for Managing Processes


After mucking a bit with system processes in Dart yesterday, I think today will be a “step back” day. Even though Dart currently lacks a hook to run code when the Dart VM exits, there is still plenty of power in the current Process class. Nevertheless, my initial assumptions when I first started on processes have been proven wrong, so now is a good time to take a look at what I really want to accomplish.

I need scripts that start, stop and (probably one to) clean up by-products of running my “real fake” web server for testing Dart HTTP clients. Rather than thinking of these in terms of how I might accomplish those scripts in the confines of a single Dart process, how might I accomplish them from a Bash script? More importantly, how might I accomplish doing so from a Bash script that is running plummbur_kruk (the name of the “real fake” library) as a package included in another package or application?

In the plummbur_kruk test suite itself, I have a similar Bash script that starts the server with:
dart test/dummy_server.dart &
I can then grab of the process ID of the forked Dart server so that I can kill it later.

That works inside the plummbur_kruk package, but I am going to need to run this from other packages, which means that I need to have my server start/stop/cleanup scripts directly in lib so that I can run them as:
dart packages/plummbur_kruk/server.dart &
And this is where the step back comes in—do I really need to require that programmers using plummbur_kruk start the Dart VM to perform these actions? On the one hand, any Dart developer will have access to Dart, so this is a little more platform independent. On the other hand, does platform independence matter in this case? I think not. I think that the primary use-case of plummbur-kruk is as part of a continuous integration script, which currently runs best on services like drone.io—services that run continuous integration in a Bash script.

So instead of mucking with processes and file system work in Dart, I think Bash is the better tool for this case.

Even though these will be Bash scripts in my Dart package, they will need to reside in the lib top-level directory, which is normally reserved for Dart code. Once installed in another project, only the lib directory is visible (at least presently). To access these scripts from within plummbur_kruk or from another package, I can invoke packages/plummbur_kruk/start.sh.

So I write a start.sh:
#!/bin/sh

#####
# Start a Kruk server

pid_file="kruk.pid"

dart packages/plummbur_kruk/server.dart &
server_pid=$!
echo "$server_pid" > kruk.pid
That writes the PID to a kruk.pid file.

In stop.sh, I read the PID and use it to kill the test server:
#!/bin/sh

#####
# Kill a running Plummbur Kruk server and to clean up any by-products

pid_file="kruk.pid"

if [ ! -e "$pid_file" ]
then
    echo "Kruk is not running"
    exit
fi

pid=$(cat $pid_file)
kill $pid

packages/plummbur_kruk/clean.sh
At the end, the stop.sh script cleans up by calling clean.sh, which will also be available from outside the package:
#!/bin/sh

#####
# Clean up any by-products of a Kruk server

rm -f test.db
rm -f kruk.pid
Armed with those three scripts, I can rewrite plummbur_kruk's tests as:
# Start the test server
packages/plummbur_kruk/start.sh

# Run the actual tests here...

# Stop the server
packages/plummbur_kruk/stop.sh
And that seems to work fairly well. Those scripts even ought to work if copied into another project's test or script directory should a programmer dislike the idea of running Bash scripts from plummbur_kruk's lib directory.

And in the end, I cannot say that I am thrilled with running Bash scripts plummbur_kruk's lib directory myself. But it does beat mucking with managing processes between Dart scripts and still reads fairly well. I will sleep on it, but this seems like a reasonable working solution.


Day #858

Tuesday, July 23, 2013

Automated Deployment of Dart Packages on a Static Site


As of last night, I have one script to automate a portion of the deployment of the ICE Code Editor. Said script examines the contents of the dart2js-generated JavaScript for inclusion in an application cache manifest file. Tonight, I hope to automate another piece of the deployment—copying packages from the system cache into my GitHub pages repository.

Dart Pub is a pretty awesome tool. Among its qualities is that it will consolidate all installed packages in a single system (or user) directory, creating a symbolic link in the current application. This saves a bit of space and ensures that all package:XXX import statement will always resolve. The only downside is that symbolic links will not work on GitHub pages. Instead, I need the real packages.

As an Emacs user, I am quite good at copying the packages directory into Emacs for a quick conversion to a rm-the-symlinks and cp-the-system-packages. But I should be able to do better.

My first thought was to parse the output of ls -l packages/ with awk. The output is standard enough that I ought to be able to do it without too much work:
➜  ice-beta git:(gh-pages) ✗ ls -l packages 
total 24
lrwxrwxrwx 1 chris chris 64 Jul 23 18:47 browser -> /home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
lrwxrwxrwx 1 chris chris 63 Jul 23 18:47 crypto -> /home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib
lrwxrwxrwx 1 chris chris 72 Jul 23 18:47 ice_code_editor -> /home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib
lrwxrwxrwx 1 chris chris 60 Jul 23 18:47 js -> /home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib
lrwxrwxrwx 1 chris chris 61 Jul 23 18:47 meta -> /home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib
lrwxrwxrwx 1 chris chris 65 Jul 23 18:47 unittest -> /home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib
But a bit of research turned up the readlink tool (I thought it was just a C command):
➜  ice-beta git:(gh-pages) ✗ readlink packages/browser 
/home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib
So my decache.sh script can iterate through each file in pub's packages directory, read the link, delete the exiting record, replacing it with the contents of the system cache:
#!/bin/sh

cd packages
for file in *; do
    link=`readlink $file`
    echo "$file ($link)"
    rm $file
    cp -r $link $file
done
And that works!

While I am at it, I ought to make the script idempotent. If I re-run it immediately after de-caching, it will copy the contents into the sub-directory, which is not quite right. All that is really needed is an if statement to exit with an error if the packages directory is already de-cached. Of course, if statements in Bash are a pain.

I think the only reason that Bash is not a more widely used language is because nobody can remember where to put the damn square brackets. It turns out that you put the damn square brackets around a boolean statement and before the semi-colon and then keyword:
#!/bin/sh

cd packages
for file in *; do
    if [ ! -L $file ]; then
        echo "Already decached."
        exit 1
    fi

    link=`readlink $file`
    echo "$file ($link)"
    rm $file
    cp -r $link $file
done
The -L test operator returns true if the argument is a symbolic link. If it is not—it the packages sub-directory is already de-cached, then I exit with non-zero status.

That does the trick:
➜  ice-beta git:(gh-pages) ✗ ./decache.sh             
browser (/home/chris/.pub-cache/hosted/pub.dartlang.org/browser-0.6.5/lib)
crypto (/home/chris/.pub-cache/hosted/pub.dartlang.org/crypto-0.6.5/lib)
ice_code_editor (/home/chris/.pub-cache/hosted/pub.dartlang.org/ice_code_editor-0.0.9/lib)
js (/home/chris/.pub-cache/hosted/pub.dartlang.org/js-0.0.24/lib)
meta (/home/chris/.pub-cache/hosted/pub.dartlang.org/meta-0.6.5/lib)
unittest (/home/chris/.pub-cache/hosted/pub.dartlang.org/unittest-0.6.5/lib)
➜  ice-beta git:(gh-pages) ✗ ./decache.sh             
Already decached.
➜  ice-beta git:(gh-pages) ✗ echo $?
1
That seems a good stopping point for tonight. Back to OSCON partying… er, preparation!


Day #821



Monday, July 22, 2013

Auto-Generating Appcache Manifests for Dart Applications


With travel and prep for OSCON, I am going to try to keep posts brief this week. Still, the gods of the chain must be appeased, so posts there must be. In today's, I hope to nail down one of the two manual processes in deployment of the ICE Code Editor: generating the application cache manifest file (the other is copying Dart packages from system cache).

Application cache is a newish feature of modern browsers that allow sites to cache files in browsers. This is done with a manifest attribute on the <html> tag of a page:
<!DOCTYPE html>
<html manifest="editor.appcache">
  <!-- ... -->
</html>
The format of the appcache file is a plain text file. Inside, it typically contains three sections: the manifest (opens the manifest and contains meta-information), the cache (contains a list of files to be cached) and a listing of files that should be fetched from the network rather than cached (typically a wildcard to denote everything not explicitly listed as being cached).

I am going to build this with a Bash script. Sometimes there is nothing better than a simple Bash script to get the job done. I toyed with the idea of a script to replace just a portion of the existing appcache file, but complete regeneration seems more robust. If I ever have to make a change to the structure of the appcache file, I will not have to worry about breaking this script by moving around important anchor text.

I start by declaring a variable to hold the name of the appcache file:
#!/bin/sh

APPCACHE=editor.appcache
I have no intention of changing the name of the file—I use a variable as a matter of taste to keep the rest of the code clean.

Next up, I wipe the old appcache file and write the manifest section of the file:
##
# Manifest
echo 'CACHE MANIFEST' > $APPCACHE
date +'# %Y-%m-%d %H:%M:%S' >> $APPCACHE
echo >> $APPCACHE
I start with a single file redirect (>) so that echo 'CACHE MANIFEST' overwrites the previous contents of the file. All subsequent operations will be done with the append redirect (>>). I am not worrying about making a backup because the generated code will be checked into source code—I already have a backup.

After the manifest directive, I add a comment containing the date to the appcache file—the date command's support for strftime-like formatting comes in handy here. Browser application cache does not use this information—the leading pound sign is a comment in appcache files. The purpose of the time stamp is solely to force the web server to respond with a 200 instead of 304. That is, a change in the content of the file—even a comment—will tell the server that this appcache file has changed and that it should not respond that it is NOT MODIFIED (304).

It is vitally important that this timestamp change whenever cached files change. If the server replies that the appcache file is unchanged, then no updated files will be loaded by the browser. I could change each and every file that is listed in the appcache manifest, but if the appcache file is unchanged, the browser will continue to use the old versions. If files are added or subtracted from the list of cached files in the appcache file, that would count as a change to the appcache file. More often than not, changes involve only modifying existing files. And a timestamp comment is the best way to communicate that some change awaits.

With the preliminary work out of the way, it is time for the cached content. There are three different kinds of content that I want cached for the ICE Code editor: libraries used while editing code in ICE (e.g. Three.js, Tween.js), files explicitly referenced in SCRIPT tags of the ICE application web page, and the JavaScript files that actually give ICE its functionality.

For the first section of cache—the files I want to make available to the programmers using ICE to make 3D animations—I list them manually:
##
# Cache

echo 'CACHE:' >> $APPCACHE

# Fixed files used within the editor for creating Three.js worlds:
cat <<EOF >> $APPCACHE
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

EOF
I am cating a HEREDOC to accomplish this. The cat command would normally echo the contents of a file. By supplying it with a HEREDOC, I tell cat to read from here.

Next, I have to echo the main.dart file into the list of cached files in editor.appcache:
# Dart script referenced in a SCRIPT tag:
echo main.dart >> $APPCACHE
echo >> $APPCACHE
No production browser in the world is going to do anything with that file, but it is explicitly referenced in a SCRIPT tag. For this reason, it must be in application cache to prevent a network request.

Last, I use my find + grep chain from the other day to find the JavaScript files—both generated by Dart and included from the ICE library—and place them in the list of cached files:
# Dart and JS code used to make the editor:
find | \
  grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
  grep -v -e unittest -e /lib/ | \
  sed 's/^\.\///' \
  >> $APPCACHE
I add a small sed script to the chain to remove leading ./ from the find output:
./part.js
./index.html
./main.dart.js.map
...
That does it for the CACHED section of my application cache file.

All that remains is a wildcard entry in the NETWORK section:
##
# Network
cat <<EOF >> $APPCACHE

NETWORK:
*
EOF
That is all there is to it. After running the script, the only difference in editor.appcache is the timestamp:
➜  ice-beta git:(gh-pages) ✗ git diff -u editor.appcache
diff --git a/ice-beta/editor.appcache b/ice-beta/editor.appcache
index 90d2ba8..ebaedef 100644
--- a/ice-beta/editor.appcache
+++ b/ice-beta/editor.appcache
@@ -1,5 +1,5 @@
 CACHE MANIFEST
-# 2013-07-20
+# 2013-07-22 10:26:40

 CACHE:
 /favicon.ico
So I have successfully written an automated script that ought to keep up with the changing files in ICE. The full, working script is:
#!/bin/sh

APPCACHE='editor.appcache'

##
# Manifest
echo 'CACHE MANIFEST' > $APPCACHE
date +'# %Y-%m-%d %H:%M:%S' >> $APPCACHE
echo >> $APPCACHE

##
# Cache

echo 'CACHE:' >> $APPCACHE

# Fixed files used within the editor for creating Three.js worlds:
cat <<EOF >> $APPCACHE
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/Mouse.js
/Scoreboard.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js

EOF

# Dart script referenced in a SCRIPT tag:
echo main.dart >> $APPCACHE
echo >> $APPCACHE

# Dart and JS code used to make the editor:
find | \
  grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
  grep -v -e unittest -e /lib/ | \
  sed 's/^\.\///' \
  >> $APPCACHE

##
# Network
cat <<EOF >> $APPCACHE

NETWORK:
*
EOF
That is a fine stopping point for today. Up tomorrow, I will try to automate the copying of system pub packages into a deployment.

Day #820

Saturday, December 1, 2012

Deploying All Dart Pub Dependencies to Heroku: Take 2

‹prev | My Chain | next›

Yesterday, I was able to deploy my full-stack Dart application to Heroku, thanks to the heroku-buildpack-dart. That buildpack installed Dart Pub dependencies on the server-side. I forked the buildpack so that I could get it to install client-side dependencies as well.

My fork was coupled to the current layout of my Dart Comics sample app. Today, I would like to generalize it to work regardless of layout. Ultimately, I will submit a pull request back to the original repository, but mostly I do not want to be tied to my current layout.

As far as I know, the only way to test buildpacks is to push a change to a repository using said buildpack. I am new to the world of Heroku buildpacks, so there may be a better way, but this is the limitation that I have to live with for now. As such, I hope to perform very few actual deploys. Instead, I write a bash script to run locally. Once it does what I expect, I will copy it in the buildpack's compile script.

What I need is for the buildpack to run through my entire app looking for Dart Pub pubspec.yaml files. In any directory with one such file, I want the buildpack to run pub install. Lastly, if that directory also has a web sub-directory, I need the buildpack to create a relative symbolic link (otherwise it results in a broken link to a build path).

Starting with just running pub install in directories with pubspec.yaml files, I use the following bash code:
root_dir=`pwd`

for filename in `find . -name pubspec.yaml`; do
    pub_dir=`dirname $filename`
    echo "Found pubspec.yaml in $root_dir/$pub_dir"
    cd $root_dir/$pub_dir
    pub install
done
Running that in my local copy of Dart Comics, I see that it does, in fact, find both pubspec.yaml files and install the requisite dependencies:
➜  dart-comics git:(master) ~/bin/pub.sh
Found pubspec.yaml in /home/chris/repos/dart-comics/.
Resolving dependencies...
Dependencies installed!
Found pubspec.yaml in /home/chris/repos/dart-comics/./public/scripts
Resolving dependencies...
Dependencies installed!
To check for the existence of the web sub-direction, I use the -e bash function:
root_dir=`pwd`

for filename in `find . -name pubspec.yaml`; do
    pub_dir=`dirname $filename`
    echo "Found pubspec.yaml in $root_dir/$pub_dir"
    cd $root_dir/$pub_dir
    pub install

    if [ -e $root_dir/$pub_dir/web/packages ]; then
        cd $root_dir/$pub_dir/web
        echo -n '*** '
        pwd
    fi
done
And that also does the trick as I see my multi-asterisked tracer bullet:
➜  dart-comics git:(master) ~/bin/pub.sh
Found pubspec.yaml in /home/chris/repos/dart-comics/.
Resolving dependencies...
Dependencies installed!
Found pubspec.yaml in /home/chris/repos/dart-comics/./public/scripts
Resolving dependencies...
Dependencies installed!
*** /home/chris/repos/dart-comics/public/scripts/web
Now for the moment of truth, I adopt this in the buildpack's compile script:
message "-----> Install packages"

cd $BUILD_DIR

for filename in `find . -name pubspec.yaml | grep -v dart-sdk | grep -v pub-cache
`; do
    pub_dir=`dirname $filename`
    message "*** Found pubspec.yaml in $BUILD_DIR/$pub_dir"
    cd $BUILD_DIR/$pub_dir

    #start pub from the /app folder to have correct symlink paths
    /app/dart-sdk/bin/pub install

    if [ -e $BUILD_DIR/$pub_dir/web/packages ]; then
        cd $BUILD_DIR/$pub_dir/web
        ln -sf ../packages
        message "Fixed web symlink"
    fi
done
For the most part this is a combination of my test bash script and my work from yesterday. The only slight change is the inclusion of two grep -v (exclude) statements in the find command. With those, I hope to avoid installing SDK and already installed Pub dependencies. I commit that to my fork, make a small change to Dart Comics and:
➜  dart-comics git:(master) gp heroku master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 316 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)

-----> Heroku receiving push
-----> Fetching custom git buildpack... done
-----> Dart app detected
-----> Installing Dart VM, build: latest
-----> Copy Dart binaries to app root
-----> Install packages
*** Found pubspec.yaml in /tmp/build_6bf4s9ejuogj/.
Resolving dependencies...
Dependencies installed!
*** Found pubspec.yaml in /tmp/build_6bf4s9ejuogj/./public/scripts
Resolving dependencies...
Dependencies installed!
Fixed web symlink
-----> Discovering process types
       Procfile declares types -> web
-----> Compiled slug size: 6.2MB
-----> Launching... done, v15
       http://dart-comics.herokuapp.com deployed to Heroku
Yay! That looks to have done the trick. A quick load of the site in Dartium verifies that all is well -- both with the client-side and server-side packages. I do believe that I am good to go.


Day #586