Monolune

Automatically restarting Racket programs when files change

The usual workflow of Racket web development is to write some code, start the racket web application, make changes to the code, stop the Racket web application using Ctrl + c, and repeat. Having to manually stop the racket program and having to restart it is tedious and feels repetitive after some time. Ideally, for increased productivity, the program should automatically restart when the source code changes (i.e. on every save).

Racket does not come with built-in autoreload functionality that can help with this, so we can only resort to a custom solution. In this guide, I will share a simple bash script solution that restarts the program when it detects that the source file has changed. As long as you are on a unix-like system with bash installed, the solution presented should work.

Here's the script:

#!/bin/bash

function get_timestamps {
    # Find all racket files in the current directory and its subdirectories,
    # then 'return' their respective modification times using stat.
    find . -type f -name '*.rkt' -print0 |
        xargs -0 stat --format '%Y'
}

timestamps="$(get_timestamps)"
while true; do

    # Start the racket program.
    # Note: replace 'server.rkt' with the name of your program.
    racket server.rkt &
    pid=$!

    # Block while waiting for file changes.
    while true; do
        # Check for file changes.
        current_timestamps="$(get_timestamps)"
        if [ "${current_timestamps}" != "${timestamps}" ]; then
            timestamps="${current_timestamps}"
            break
        fi

        sleep 0.5  # So that we do not loop too fast.
    done

    # Stop the racket program because file changes have been detected.
    kill -SIGINT ${pid}  # Equivalent to pressing ctrl-c.
done

I usually save the script as runserver, and place it in the root directory of the web application projects I work on. Remember to make the script executable (chmod u+x runserver); that way, the script can be started using ./runserver from the terminal.

The script assumes that the program name is server.rkt. The program will be reloaded when any file with an .rkt extension has changed in the current working directory or in any of its subdirectories. Ctrl + c stops the bash script, and, by extension, the Racket program itself.

server.rkt is of course assumed to be a program that starts the racket web server, either by using the serve/servlet procedure or the web-server/insta language. For example:

#lang racket
(require web-server/servlet-env)  ; For serve/servlet.

; Web server code ...

(serve/servlet (lambda (request)
                 ; Handle request ...)
               #:listen-ip "127.0.0.1"
               #:port 8000
               #:launch-browser? #f
               #:quit? #f)

You may object to polling method (i.e. the infinite loops) on the grounds that it is improper or inefficient. Those are valid concerns. There exist proper ways of detecting file changes, but those methods are usually less portable. One alternative to the manual polling method is to use inotifywait, which makes use of the Linux inotify interface. inotifywait can be installed using sudo apt-get install inotify-tools on Debian and Ubuntu. I leave it to you to decide if you prefer the polling method, or the 'proper' method for detecting file changes.

Overall, I find that the autoreload bash script has been indispensable for getting quick feedback from changes made to the source code during web development, as there is no longer any need to manually restart the web server. I believe it will be useful to most people too.