Skip to content

Comments

feat: Adding WASM build target#277

Merged
yhakbar merged 19 commits intomainfrom
feat/adding-wasm-target
Feb 23, 2026
Merged

feat: Adding WASM build target#277
yhakbar merged 19 commits intomainfrom
feat/adding-wasm-target

Conversation

@yhakbar
Copy link
Contributor

@yhakbar yhakbar commented Feb 19, 2026

Description

I was able to play around and get a WASM build target working pretty quickly.

Works pretty well:

Screenshot 2026-02-19 at 15 39 43

While going through, there were two issues that stuck out and I addressed them while I was there.

The WASM binary being produced was 83MB. That was significantly larger than it needed to be. After digging into it a bit, I was able to work out that a lot of that bloat was from imports of things like go-getter and go-commons in the critical path used for rendering templates when using Boilerplate as a library.

Those things weren't critical to usage of Boilerplate as a library, so I moved out the integration with go-getter to a package that wouldn't get imported by any code used by RenderTemplateFromString. That prevented it from getting compiled into the WASM binary. This should also make Boilerplate a much lighter import for consumers that don't depend on go-getter functionality.

While doing that, I also removed the dependency on go-commons, go-errors, and all of the transient dependencies they pulled in. At that stage, the binary size had dropped to 15MB.

After that, I also addressed gopls findings to modernize the codebase a bit, as we don't address that very often here.

Also cut out the color dependencies, go-cmp, and did some reorganization to make it easier to avoid importing bloat into the WASM binary. Size dropped to 13MB.

Cut a pre-release here:
https://github.com/gruntwork-io/boilerplate/releases/tag/v0.12.0-alpha1

With brotli compression it drops to 2MB.

https://github.com/gruntwork-io/boilerplate/releases/tag/v0.12.0-alpha3

TODOs

Read the Gruntwork contribution guidelines.

  • Update the docs.
  • Run the relevant tests successfully, including pre-commit checks.
  • Ensure any 3rd party code adheres with our license policy or delete this line if its not applicable.
  • Include release notes. If this PR is backward incompatible, include a migration guide.

Release Notes (draft)

Added / Removed / Updated [X].

Migration Guide

@vercel
Copy link

vercel bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
boilerplate Error Error Feb 23, 2026 4:27pm

Request Review

@yhakbar yhakbar requested a review from josh-padnick February 19, 2026 20:44
@josh-padnick
Copy link
Contributor

First, this is my first experience seeing an actual implemented WebAssembly module; very cool! I love how you're able to call a Go function directly from javascript code, and this is quite a bit of added value for so little code.

Since it was vibe-coded, I'm guessing you tagged me for review not to check out the code, but to comment on the general idea and how it relates to Gruntwork Runbooks?

My experience with Runbooks is that the initial problem seems straightforward -- render a web UI for boilerplate -- but the rabbit hole goes deep. I think the first thing I built was something that renders a web form based on a boilerplate.yml. But then you realize you need some guiding text to help the user understand the context. And then there are external commands you need to run to populate some of the variable values. And for those you need auth, and then you want to pass values from one command to another, and render files, and see which files you render.

Runbooks does all of that and more already but would be non-trivial to host in a commercial platform, whereas this is much simpler but seems straightforward to host in a commercial platform if you keep the scope narrow.

I'm not sure if that's the feedback you were looking for, so let me know if I'm off base!

@yhakbar
Copy link
Contributor Author

yhakbar commented Feb 20, 2026

@josh-padnick

This is really just me trying to show that we don't need Runbooks to be a Golang application to get the benefits of leveraging Boilerplate with it. In the current split, there's a roughly equal portion of Golang to TypeScript code in Runbooks, and I imagine that most of the functionality you're interested in building out is either in the frontend TypeScript side, or could work equally well as code written in the backend using TypeScript.

The value that we get in making Runbooks an Electron app instead of keeping it a Golang server you run locally is pretty massive, IMO. I'd be happy to chat further in a call if you like.

@yhakbar yhakbar marked this pull request as ready for review February 20, 2026 19:16
@josh-padnick
Copy link
Contributor

@yhakbar Ah, got it.

The value that we get in making Runbooks an Electron app instead of keeping it a Golang server you run locally is pretty massive, IMO.

I'd be curious what you have in mind here?

To share some additional thoughts, I'm naturally hesitant to undertake a big migration since there are just so many edge cases to get right (PTY support, concurrent command execution, startup time, the CLI experience from a Node app, etc.). It's all doable, but it's just time and attention (and now tokens!). Also, I'm a volunteer here, so I have especially limited appetite for a large project that doesn't directly improve the user experience.

Migration costs aside, as with every other technical decision, I see tradeoffs between Go vs. Typescript on the backend. Here's my current thinking, though I'm sure I'm off on some of these points and welcome further discussion.

Pros

  • Unified typescript codebase. This seems like the biggest win, especially sharing types/validation schemas (I'm already using Zod in the frontend).
  • Desktop-native features. I guess this would include system tray, native menus, file associations, and notifications, though to be honest I don't see the immediate value in any of these. In fact, I think there's value to keeping the OSS tool lightweight and narrowly scoped to create a natural pull to the commercial version.
  • Smaller security surface area. I don't love the idea that Runbooks's Go backend exposes an HTTP interface, but it's also well-understood, easy to debug, and I've put a bunch of security protections in place already for it. Removing the network piece entirely from frontend <-> backend is definitely a win, though there's also more complexity in the Electron configuration itself, which I worry will be time-consuming to get right.

Cons

  • More maintenance overhead. I do worry about the added overhead Electron brings, which looks like it would involve platform-specific installers, auto-updaters, and Chromium security patching.
  • Headless runs. I see value to using Runbooks in a headless way. For example, Runbooks has a testing framework. We certainly could build a pure Node CLI, but it's more overhead.
  • Rewriting things Go handles natively. My sense is that Go has stronger native support for things like PTY execution, concurrent script execution, and environment capture, so I do worry about getting this right with a Node backend given how fundamental file system and environment management is to Runbooks. I also worry about Node's cross-platform support vs. Go's.
  • Startup time and resource usage. The Go binary starts in milliseconds and uses ~30MB of RAM. Will Electron take a few seconds to start and use a bunch of RAM?
  • Boilplerate + WASM. This is more work, so I'm listing it here but it doesn't seem too hard. I do wonder about boilerplate edge cases, though. For example, can a WASM build of boilerplate execute commands, get the output, and then interpolate those into templates?

Overall thoughts

I'm wrapping up one more major feature for Runbooks soon (the ability to open a Runbook that's dynamically built based on an OpenTofu module). Then I've got some docs updates and demos to record. Then I think it'd be a great time to chat since you'll have full context of what the tool does today. I'll also share some thoughts on long-term vision internally; there are some really interesting ideas here IMO.

Perhaps chat end of next week? Let's coordinate via internal Slack.

NonInteractive: true,
NoShell: true,
OnMissingKey: options.ExitWithError,
OnMissingConfig: options.Ignore,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we initialize ShellCommandAnswers field?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it can be used, but I can double-check.

if IsDir(path) {
const defaultDirPerm = 0755
return os.MkdirAll(filepath.Join(targetFolder, relPath), defaultDirPerm)
} else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant 'else' in 'if'

package color

const (
ansiReset = "\033[0m"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this work on WASM environment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't matter. It doesn't get imported or used there.

@yhakbar yhakbar merged commit 3366216 into main Feb 23, 2026
29 of 30 checks passed
@yhakbar yhakbar deleted the feat/adding-wasm-target branch February 23, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants