Yew on Gitlab pages
My latest side-project requires a web front-end.
I’ve heard there are many frameworks in many languages for this. And to be honest, this is an understatement.
I first went for Svelte
because a friend recommended this to me. I then had some issues with it an tried Solid
which was also a no-go in the end. Both are Javascript
framework and the node_module
hell is actually a thing.
I may have mentioned it before on this site but: I enjoy Rust
🦀 quite a lot. So I searched for a solution on this side of the force and found out that Yew
has matured quite a lot since I first saw it.
This is the technical solution I went with. And along the way, someone on Discord asked for a cheap way to host such an application. Here is my take at this question: can’t make cheaper than free thanks to Gitlab
pages.
For the record, this very website is also hosted for free on
Gitlab
pages. It uses theZola
Static Site Generator which is written inRust
🦀
The stack
Yew
What is Yew
? (Baby don’t hurt me 🎶 No more 🎵)
Yew
self describes itself as A framework for creating reliable and efficient web applications.. It’s written in Rust
🦀 and is heavily inspired by both React
syntax-wise and Elm
architecture-wise.
In practice, an HTML
page will load a WASM
file and bootstrap it using a very tiny piece of Javascript
. Both the WASM
and JS
files are automatically generated so all that is left to us is the Rust
🦀 code.
Gitlab pages
Gitlab
allows anyone with an account to host publicly available pages for free.
To take advantage of this, one has to write a CI/CD pipeline with a pages
job that outputs a public
directory as part of its artifacts.
So, all that is left is to compile a Yew
application in a Gitlab
pipeline. Well, almost.
The implementation
I’m not going to explain how to write a Yew
application here. The Yew
’s website has a both a tutorial and an extensive documentation. On top of it, the Yew
’s repository contains a plethora of well written examples.
One thing to note though: while Yew
supports Server Side Rendering, Gitlab
pages only serve static content. This implies the application will only use Client Side Rendering.
Actually, there are two things to know before continuing. The Yew
documentation makes use of the BrowserRouter
. This is a valid choice for anyone who can set-up its own reverse proxy to serve the index.html
page from every URI. But Gitlab
pages does not allow such a fine-grained configuration. The HashRouter
has to be used instead of the BrowserRouter
for the application to be served the right way.
Pipeline
A first version of the pipeline could look like this :
1 pages:
2 stage: deploy
3 image: rust
4 before_script:
5 - rustup target add wasm32-unknown-unknown
6 - cargo install --locked trunk
7 script:
8 - trunk build --release --dist public --public-url $CI_PROJECT_NAME
9 artifacts:
10 paths:
11 - public
Let’s break it into pieces.
- The job’s name: is has to be
pages
for this to work. - The stage can be left out as it defaults to
test
but I usedeploy
as it’s what the job does after all. - The
rust
image is used for compilation of theYew
project. - Before actually compiling, some dependencies are needed: the
wasm32
target to compile fromRust
🦀 toWASM
andTrunk
, theWASM
bundler recommended byYew
. - The compilation is a simple process: tell
Trunk
to build the application in thepublic
directory and use the$CI_PROJECT_NAME
name as the URI the application will be served. - All is left is to tell
Gitlab
that there is apublic
repository to serve.
Done, it works!
But damn, it’s slow! Imagine waiting almost 15 minutes to deploy a simple webapp… Let’s speed things up!
Custom docker image
The previous pipeline spent around 10 minutes ensuring dependencies were available (the before_script
part). And only a couple of minutes compiling the actual project (the script
part).
What if the dependencies were already available?
1 FROM rust:latest
2 RUN rustup target add wasm32-unknown-unknown
3 RUN cargo install --locked trunk
docker build -t registry.gitlab.com/$PROJECT .
docker push registry.gitlab.com/$PROJECT
Updated pipeline
It’s now possible to use the newly built and pushed Docker
image. Thanks to it, both the wasm32
target and trunk
are ready to use in a blink.
1 pages:
2 stage: deploy
3 image: $CI_REGISTRY_IMAGE
4 script:
5 - trunk build --release --dist public --public-url $CI_PROJECT_NAME
6 artifacts:
7 paths:
8 - public
The job now takes less than three minutes to succeed.
The final improvement
1 pages:
2 stage: deploy
3 image: $CI_REGISTRY_IMAGE
4 script:
5 - trunk build --release --dist public --public-url $CI_PROJECT_NAME
6 - cp public/index.html public/404.html
7 artifacts:
8 paths:
9 - public
The sixth line copies the index.html
into a 404.html
file. This is needed because Gitlab
will serve the 404.html
page if someone browse to any invalid page.
Usually, the 404
page is carefully design along with other pages. But here, the HTML
code is generated by the webapp. So duplicating the code from the index.html
ensure the user always land on a working app.
Now that I write those lines, I might be tempted to change the cp
into an mv
. After all, if there is no index
, the 404
will do just fine in any circumstances.
Conclusion
Hosting a Yew
application is cheap as free on Gitlab
pages. All it takes is a working Rust
🦀 application and a small piece of CI/CD.
Make sure to use the HashRouter
instead of the BrowserRouter
and you’re done. I wrote a very simple application as a proof-of-concept here for anyone interested.