Back To Top

 How we Integrated a React App into a GO CLI
June 15, 2023

How we Integrated a React App into a GO CLI

  • 0

React App into a GO CLI

Embedding a React app into a GO CLI is a complex process that took our team close to three to four weeks to solve. Here is how our team solved it.

Embedding a React app into a GO CLI is an uncommon product decision, so before diving into the technicalities it’s worth calling out why our team made this decision.

If you are unfamiliar with Plural, it is an open-source DevOps platform that simplifies deploying open-source software on Kubernetes. Our platform is available via a cloud shell experience or through downloading our CLI experience.

While we recommend using our cloud shell experience since it comes with all the tools and dependencies needed to run Plural, users still prefer to use their own CLI for an extra added layer of security.

Previously, deploying Plural via our CLI or cloud shell was a suboptimal experience for engineers. It required a ton of re-typing, and users could not go back and update values. As a result, around 30 – 35% of users were receiving error messages during onboarding.

We knew that we needed to improve the onboarding process to increase the user success rate. At the start of the year, we completely redesigned our cloud-shell experience with the hopes of reducing error rates. A big part of this redesign was implementing an install wizard in our cloud shell. That alone led to a drop in users hitting errors in our cloud shell experience and we’re now consistently around a 90% success rate for onboarding users.

However, users still faced similar issues with our CLI deployments, the most secure and robust way to use Plural. Making this experience as seamless as good as possible was a high priority for our team. Earlier in the year, we saw a drop in user error rates when we implemented an install wizard in our cloud shell. To stay consistent in our product experience and to reduce error rates with users onboarding through our CLI experience, we implemented the install wizard in our CLI.

The only problem was embedding a React app into a GO CLI is a complex process that took our team close to three to four weeks to solve. Here is how our team solved it.

Identifying a Framework for Embedding Our UI

Before identifying a framework, I had a few requirements in mind that our solution had to contain.

The framework had to be available within Go so I could reuse our current code base which is written in Go. The main reason for this was so I could call some GO functions directly in the front end to avoid rewriting all this logic.
I needed to use React so I could reuse our design system for creating this UI in the CLI.
Early on in my research, I came across Electron and Tauri.

I had previous experience working with Electron and it is a popular solution for embedding Chromium and Node.js to create desktop applications. However, there are some major downsides to it such as it embeds the whole chromium inside making our binary file way too big which is what we were looking to avoid.

Tauri’s framework was interesting and has a ton of potential. While it’s similar to the one that we ended up choosing, it was written in Rust and didn’t allow me to reuse our current code base.

Ultimately I landed on using the Wails framework and it has been a flexible solution for our team so far. Wails is a project that enables you to write desktop apps using Go and web technologies. A big reason for choosing Wails was that it automatically makes Go methods available to JavaScript, allowing us to call them by name from our front end.

Challenge 1: Re-Configuring our GitHub Deployment Pipeline

Since Plural does not provide the UI for all architectures (we only support a handful of architectures), I had to build a Go build pipeline and rework from scratch our GitHub deployment pipeline that we also use for our CLI.

To achieve this I had three parallel builds at the same time using Windows, Linux, and MacOS host machines thanks to the GoReleaser pro “split” feature.

The first job matrix runs all jobs in parallel at the same time on three separate hosts OS (Windows, Linux, Darwin) and builds the result binaries. It is then picked up by the next step (release) and it takes care of uploading artifacts to our GitHub release and updating our upstream brew artifact. The last part takes care of uploading dockerized images w/ CLI that we can further use (i.e. in the Cloud Shell in the Plural app.)

The below code uses GoReleaser pro split build feature and our conditional tag-based Go build pipeline. Depending on the architecture it will either build a CLI binary with our without embedded.

Challenge 1: Re-Configuring our GitHub Deployment Pipeline - React App into a GO CLI

Challenge 2: Configuring and Running the UI After typing Plural Install

After a user runs Plural Install in the CLI, the UI opens up to allow the user to select the applications they want to install. By default, Wales recommends building a standard binary that when executed opens the UI. In reality, it’s not destined to work directly with CLIs.

To solve for this I ended up creating a custom Golang pipeline using build tags to include the UI when we only type in Plural Install.

To conditionally embed the ‘install’ command into the CL during compilation we pass `-tags ui` or `-tags generate` to the `go build` command. Once I had this set up I then added this code to embed the code that actually handles the UI conditionally. This code block below simply opens the actual UI windows when you call `plural install`. It uses build constraints (tags) to only include this code when we need it.

I then ensured that the tags mentioned above are not provided during the build process. If they aren’t, this code will be embedded so that the build will not fail.

Next, I had to conditionally embed code that runs the UI in order to generate bindings that allow us to call Go methods in the UI to generate bindings that allow calling Go methods in the UI. The below code generates frontend bindings used to only call the backend when a ‘generate’ tag is provided during the build process.


Challenge 3: Refactoring our GO project

Early on one thing that I overlooked was how much we were going to need to refactor how our team set up our Go project. Basically, we had to extract our application entry point in our main package from the `cmd/plural` directory to the root of the project since Wails currently does not play well with different setups. To do this it required both code refactoring and updating all our build CI/CD pipelines to work smoothly with the new code layout.

Along the way, there ended up being a good amount of smaller problems that we needed to quickly solve such as running our CI/CD. Previously, we simply would use `go build` on any host for cross-building our CLI. On the other hand, Wails required the ‘CGO_ENABLED=1’ flag when building the application and that forced us to actually run the build for target architecture on the target host OS.

Thankfully, GitHub Actions allow us to do just that and run the build natively on every OS. There are still ways to avoid that but when I looked into them, they were much more complicated than the solution I have decided to go with.

Another problem that I encountered was how to call Go functions from the front-end code base. This problem ended up being easier to solve than I anticipated, and I followed Wails official guidance for calling Go function bindings. To simplify this for us internally I ended up adding some wrapper code on top of what they recommended.

Challenge 3: Refactoring our GO project

Moving Forward
Overall, I enjoyed working on this complex project. If you are looking to solve a similar problem I advise that you try not to overcomplicate everything.

Unfortunately, for us, our problem was too complex and we could not follow Wails recommendations because it involved being quite familiar with Golang and using Golang build constraints. A majority of use cases will likely not be as complex as ours and you should be able to follow along with their documentation and be good to go.

I hope you can learn a bit from this, and please do not hesitate to reach out if you have any additional questions about how we solved this as an engineering team.

To learn more about how Plural works and how we are helping engineering teams across the world deploy open-source applications in a cloud production environment, reach out to our team for more information.

Prev Post

How to Build a platform that open sources itself

Next Post

Swift OpenAPI Generator Aims at Streamlining HTTP Client/Server Communication


Leave a Comment