--- title: "Migrate from Ghost to Hugo" date: 2024-02-17 draft: true slug: "migrate-from-ghost-to-hugo" tags: ["ci/cd", "docker", "git", "hugo"] type: "programming" --- ## Current solution I've had a blog since early 2022. Historically, I chose Ghost to make this site. At the time, several factors led me to choose this CMS rather than another: - Ease of deployment and use - Online administration page and article editor - Simple, modern theme - Regular updates But after using it for quite some time, a number of problems have arisen that can't really be corrected: - Limited customization - Complex theme modification if you want to be able to update it afterwards - Significant resource requirements for Ghost+Mysql (see Conclusion) - No options for advanced content organization - Too many unused options to justify this choice (subscription, user account, etc.) All these problems, combined with the fact that my needs had evolved, led me to change the technical solution for my blog. ## Choosing a new solution Today, there are many options for blogging. Third-party hosted options like Medium, CMS like Wordpress and Ghost, but also static websites generators. For this new version of my blog, I've opted for a static websites generator. Here again, there are several solutions, but I've settled on [Hugo](https://gohugo.io/). Hugo is a GO-based opensource framewok created in 2013. It's known for being very fast, highly customizable and with a very active community. After choosing Hugo I had to choose a theme, I had several requirements in terms of features. I ended up choosing [Blowfish](https://blowfish.page/). It's a highly flexible and customizable theme, regularly updated, with a minimalist, modern design. ## Migration ### Settings ### Contents and optimization ```bash find ./content/ -type f -name '*.png' -exec sh -c 'cwebp -q 90 $1 -o "${1%.png}.webp"' _ {} \; ``` https://pawelgrzybek.com/webp-and-avif-images-on-a-hugo-website/ ## Storage and automatic deployment ### Git-LFS ```bash apt install git-lfs git lfs install git lfs migrate \ import \ --include="*.jpg,*.svg,*.ttf,*.woff*,*.min.*,*.webp,*.ico,*.png,*.jpeg" \ --include-ref=refs/heads/main ``` ### CI/CD hugo.Dockerfile: ```dockerfile FROM golang:1.22-alpine AS build ARG CGO=1 ENV CGO_ENABLED=${CGO} ENV GOOS=linux ENV GO111MODULE=on RUN apk update && \ apk add --no-cache gcc musl-dev g++ git RUN go install -tags extended github.com/gohugoio/hugo@v0.122.0 ``` I then use the following commands to build the Hugo image: ``` docker build -t git.d3vyce.fr/d3vyce/hugo:latest -f hugo.Dockerfile . docker push git.d3vyce.fr/d3vyce/hugo:latest ``` Dockerfile: ```dockerfile # Build Stage FROM git.d3vyce.fr/d3vyce/hugo:latest AS build WORKDIR /opt/blog COPY . /opt/blog/ RUN git submodule update --init --recursive RUN hugo # Publish Stage FROM nginx:1.25-alpine WORKDIR /usr/share/nginx/html COPY --from=build /opt/blog/public /usr/share/nginx/html/ COPY nginx/ /etc/nginx/ EXPOSE 80/tcp ``` ```yaml name: Build Blog Docker Image on: push: branches: - main jobs: build docker: runs-on: linux_amd steps: - name: checkout code uses: actions/checkout@v3 # with: # lfs: 'true' - name: Checkout LFS run: | function EscapeForwardSlash() { echo "$1" | sed 's/\//\\\//g'; } readonly ReplaceStr="EscapeForwardSlash ${{ gitea.repository }}.git/info/lfs/objects/batch"; sed -i "s/\(\[http\)\( \".*\)\"\]/\1\2`$ReplaceStr`\"]/" .git/config git config --local lfs.transfer.maxretries 1 /usr/bin/git lfs fetch origin refs/remotes/origin/${{ gitea.ref_name }} /usr/bin/git lfs checkout - name: Set up QEMU uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Login to Docker registry uses: docker/login-action@v2 with: registry: git.d3vyce.fr username: ${{ github.actor }} password: ${{ secrets.GIT_TOKEN }} - name: Build and push uses: docker/build-push-action@v4 with: context: . file: ./Dockerfile platforms: linux/amd64 push: true tags: git.d3vyce.fr/${{ github.repository }}:latest ``` https://gitea.com/gitea/act_runner/issues/164 ## Conclusion: before/after comparison ![Ghost based blog lighthouse result](img/image-1.webp) ![Hugo based blog lighthouse result](img/image-2.webp)