Deployment submodules

@notjack.space

Racket organizes a lot of its development tooling around submodules. By sticking code inside a submodule named test, the raco test tool will automatically run that code. Similarly, code inside a main submodule is run when the racket command line tool loads a module but not when one module loads another, similar to the if __name__ == "__main__": idiom in Python. Submodules were first introduced to Racket in this blog post, which outlines their purpose and unique behaviors quite well.

What else can we do with this approach?

When I make a server application with Racket, I usually structure it as a single Racket package. I compile the app by installing it with raco pkg install, and I test it with raco test. I put the server's startup script inside a main submodule, and I bundle this all up into a Docker image. Deployment consists of building the Docker image from a Dockerfile and pushing it to an image registry, relying on some container orchestration platform somewhere to see the updated image, pull it, and restart containers.

That last step, with all the futzing about in Docker, is irritating. Particularly the Dockerfile, which is just a domain-specific language for specifying a deployment script.

Now, as a Racketeer, when I see the phrase "domain specific language" and I don't see a #lang line, my eye starts twitching and my fingers start itching. What if Racket handled that part?

Suppose, just like we have raco test, we also had raco deploy. Like raco test, this would take some form of pointers to Racket code as input (filenames, installed collection names, package names, etc.). Unlike raco test, which scans the code for tests to execute, it would scan the code for deployment scripts. These would be represented by submodules named deploy. So in my server app example above, next to the server's main submodule which starts the server would be a deploy submodule which, when run, copies the package's code into a Docker image and pushes that image to a registry.

Why do this? Well, for one thing, I could now have #lang dockerfile! The language would use the same syntax as an ordinary dockerfile, except it would compile it into a deploy submodule with a script that builds the docker image itself. This wouldn't even need a dependency on Docker's build system itself either, it would just need a container runtime to execute commands in. And that's in theory possible to package up as an FFI library using Racket's package manager.

Being a #lang brings other benefits. For instance, I'd be able to reuse all of Racket's toolchain for the deployment script - no need to have docker installed at the command line. And I'd be able to edit the file with syntax highlighting in any editor that understands Racket. And I'd be able to integrate the #lang with Resyntax, to make suggestions about how to improve dockerfiles.

Submodules are a good idea. I think they have numerous under-explored use cases for industrial code, and a submodule-driven deployment system seems like an interesting approach. Somebody give it a shot please, unless you feel like waiting until I get some free time after the heat death of the universe.

notjack.space
Jacqueline

@notjack.space

rapid unscheduled torment nexus disassembly expert — nonbinary, she/they — writes Racket — @doitwithalambda on twitter

Post reaction in Bluesky

*To be shown as a reaction, include article link in the post or add link card

Reactions from everyone (0)