61
About GitLab and Pages by Safely Dysfunctional
Posted on July 6, 2021 at Safely Dysfunctional Blog by Janil Garcia in Brazilian Portuguese and translated by Eduardo Pereira Garcia
To serve from GitLab, two options are offered, the Community Edition (which is totally free) and the Enterprise Edition (which is paid, but have access to more features).
Furthermore, GitLab has a bunch of Project Management and DevOps tools. And this since before GitHub offers this kind of services. We are going to use both here: GitLab CI/CD and GitLab Pages.
This service of Continuous Integration/Continuous Delivery is offered by GitLab. Through a YAML file at root directory of the project, where a series of steps that will be executed every time a specific event occurs in the repository can be described (as a push to a branch or a merge request for instance).
It is also possible to specify the environment where these instructions will be executed, starting from a Docker image/container. Also, environment variables and secret files can be settled up, if needed. GitLab offers this service for free, with limitations though. The instance has low memory, and processing capacity to run for 1 hour only.
Your own runner (an instance able to run CI/CD) can be plugged in or additional GitLab ones can be provisioned. Well, I don't want to expend some money on it with this blog (Safely Dysfunctional) so I decided to use the free tier.
This info is relevant because Hakyll application requires to be complied before it generates the pages, and the compilation process of Haskell is a pretty expensive (computationally saying). Although, the executable is incredible fast, due to great work made by the compiler. This processing cost will be discussed soon.
Well, the GitLab Pages is a GitLab service created to hosts static pages. And guess who has static pages to be hosted? This guy (Janil points himself). - very funny bro, ha -
GitLab Pages is integrated to GitLab CI/CD, therefore it lets you generate yours pages through any SSG (Static Site Generator)1 you want, then you put the files at a web server so everyone can see.
This is very useful, especially for project documentation or a blog in my case.
The most important step is to choose the project's name used to host the site, as incredible as it seems. My username in GitLab is janilcgarcia, it is mapped the page to the project as below:
consider $user_name = janilcgarcia and $project_name = blog
If name of the project follows the pattern $user_name.gitlab.io (janilcgarcia.gitlab.io in this case), the page is hosted at janilcgarcia.gitlab.io.
If the project name is different from that it is hosted at $user_name.gitlab.io/$project_name (that is
janilcgarcia.gitlab.io/blog
).
There is an extra detail, GitLab supports the groups concept, which is and underlying feature especially for related projects and people.
Each group receive a domain as following form $group.gitlab.io
(in my case https://safelydysfunctional.gitlab.io) to host static pages too. Using this you can use legal domains where to serve your site. The mapping for groups works as previously described for users.
In GitLab Pages' website, it is possible to configure your own page accordingly with the desired tool, but the idea is:
# You start setting the image for the runner
image: docker/base/image
# Then you define the steps to generate the pages
pages:
# on `script` you put the steps necessary to build the page
script:
- ssg compile -o public/
# on artifacts you specify files that should be kept after the execution
# the pages MUST be on the public/ directory in project root
artifacts:
paths:
- public
# with `only` you can specify which branches trigger this action
only:
- master
Do you remember I said that Haskell compilling takes time. Very well, if you take the obvious choice to get a Haskell docker image, compile the project and use the binary to construct static pages, the task will fail. After one hour I've learned this at hardest way.
The site is running, so you know I found another path, right?
Well, for a time I was creating an image with the pre-compiled binary, therefore I could run the compiler in my PC, which have more processing power and after that doing the upload to Docker Hub.
This approach works very well, because the binary does not change frequently, only when you change something in its code.
However it has the disadvantage to requires an image creation in my PC, sending to Docker Hub and having to do it every time that I changed the code. But I was aware of another approach that Haskell guys are using, the nix.
Nix is the NixOS package manager, but it runs in other distros too (I believe it does even on MacOS). The great difference of Nix is that it isolates the packages from the systems in specific directories and after that hooks them in a very compatible manner when needed.
This allows multiple versions of same packages available in the system. And to drive this integration, Nix uses a declarative language (purely funcional). Thus all OS environment becomes immutable and installing a package is nothing more then apply a function that updates this environmento to a new state that have a package.
THe perfect companion to Haskell, not even the OS itself has side effects.
Looks like great, right? But a weak point is the documentation shortage, help is hard to find and understand how the package menager works is not an easy task too.
Fortunatelly I found a tutorial that gave me a north.
https://cah6.github.io/technology/nix-haskell-1/
After a long fight and deep search I ended with a 3 files repository config (blog.nix
, default.nix
and shell.nix
):
blog.nix
describes a new package based on a project using cabal
{ mkDerivation, base, binary, bytestring, data-default, directory
, filepath, fsnotify, hakyll, http-types, lib, pandoc, pandoc-types
, process, tagsoup, text, wai, wai-app-static, wai-extra, warp
, pythonPackages
}:
mkDerivation {
pname = "blog";
version = "0.1.0.0";
src = ./.;
isLibrary = false;
isExecutable = true;
executableHaskellDepends = [
base binary bytestring data-default directory filepath fsnotify
hakyll http-types pandoc pandoc-types process tagsoup text wai
wai-app-static wai-extra warp
];
executableSystemDepends = [ pythonPackages.pygments ];
license = lib.licenses.gpl3;
hydraPlatforms = lib.platforms.none;
}
default.nix
describes how to compile the project and defines the environment where the compilation must be done.
let
pinnedPkgs = import ./pkgs-from-json.nix { json = ./release-21.05.json; };
in
pinnedPkgs.pkgs.haskellPackages.callPackage ./blog.nix { }
at last shell.nix
describes that nix-shell can start from inside the same environment that th compilation one. It is from here you could put hoogle and haskell-language-server if needed.
{ }:
(import ./default.nix).env
Now, if you need to know how this works, I'm the wrong guy to ask. I know it works, and that's all.
A detail I needed to figure how to implement out was install pygments inside the project's compilation environment. To do that, I had to modify the file blog.nix
and add the property executableSystemDepends
containing pythonPackages.pygments
, the pygments package name in Nix.
I believe that, maybe, this is not the right place for this package to be installed (still this is for sure a project dependency). Perhaps I should have inserted it in shell.nix
, but until I figure this whole thing out that's how it'll remain. Most importantly: it works as is, so let's try not to go fixing what ain't broke.
Once a functional environment have been created in Nix, using it in GitLab CI/CD is easy.
That's all I needed:
image: nixos/nix
pages:
before_script:
- nix-build default.nix
script:
- nix-shell --run 'result/bin/site build'
artifacts:
paths:
- public
only:
- master
Very simple, isn't it? It uses nixos/nix
avaiable at Docker Hub (which is very small, containing only the package manager - not even bash
seems to be there), it compiles the site generator using the description into default.nix
and after it generates the site, storing the result in the public directory, that will be collected by GitLab Pages and goes to web.
The hardest part of it all is to make Nix works. GitLab CI and GitLab Pages are very simple and flexible. An this post encloses the Hakyll's legend, for now!!! hehehe
"That's all folks!"
61