No description
  • HTML 43.5%
  • CSS 25.5%
  • Elixir 25.5%
  • JavaScript 3.6%
  • Dockerfile 1.9%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Hocine HAMMOUCHE d405dbb530
All checks were successful
Release / publish-image (push) Successful in 3m50s
release: bump version to 1.0.0
2026-10-10 18:19:24 +02:00
.forgejo/workflows release: bump version to 1.0.0 2026-10-10 18:19:24 +02:00
config feat: port the site from Go to Elixir/Phoenix 2026-10-10 17:55:22 +02:00
lib feat: port the site from Go to Elixir/Phoenix 2026-10-10 17:55:22 +02:00
priv feat: port the site from Go to Elixir/Phoenix 2026-10-10 17:55:22 +02:00
.dockerignore build: Elixir release image, compose and CI 2026-10-10 17:55:22 +02:00
.formatter.exs feat: port the site from Go to Elixir/Phoenix 2026-10-10 17:55:22 +02:00
.gitignore feat: port the site from Go to Elixir/Phoenix 2026-10-10 17:55:22 +02:00
docker-compose.yml build: Elixir release image, compose and CI 2026-10-10 17:55:22 +02:00
Dockerfile build: Elixir release image, compose and CI 2026-10-10 17:55:22 +02:00
LICENSE docs: add AGPL-3.0 license file 2025-12-31 17:57:52 +01:00
mix.exs release: bump version to 1.0.0 2026-10-10 18:19:24 +02:00
mix.lock feat: port the site from Go to Elixir/Phoenix 2026-10-10 17:55:22 +02:00
README.md build: Elixir release image, compose and CI 2026-10-10 17:55:22 +02:00

My portfolio and resume

An Elixir (Phoenix) website with a bilingual (English and French) portfolio and CV, served from priv/content/*.json. There is no database, session or JavaScript build step.

Layout

A standard Phoenix application without a database:

  • lib/portfolio/ holds the content (Content, strictly validated at boot), dates, translations, hashed assets and the CV cache; lib/portfolio_web/ holds the router and controllers, with the HEEx templates in controllers/page_html/.
  • priv/content/<lang>.json is the content and priv/static/ the static files.
  • Dates in the content are ISO 8601 (2023-09) or present.

Development

docker compose up --watch     # http://localhost:8080, reloads on changes

mix format formats the Elixir code and the HEEx templates.

CVs

/cv-en.pdf and /cv-fr.pdf are rendered on demand from cv.html.heex with headless Chromium (through ChromicPDF) and cached in memory for 24 hours. Renders run in the background: at boot, and again once a PDF has expired (the old one is served meanwhile). A failed render is not retried for 30 seconds and returns 503 when there is no PDF, and HEAD answers from the cache without starting a render.

Deployment notes:

  • Chromium is required: the server looks for it at startup (CHROMIUM_PATH, or a chromium / chromium-headless-shell executable on the PATH) and refuses to start without it.
  • Chromium's sandbox needs user namespaces, which Docker's default seccomp profile denies. Run the container with a profile that allows them, or set CHROMIUM_NO_SANDBOX=1 (only in an isolated container); otherwise CV requests return 503 and the log says why.
  • Give the container at least 60 seconds to stop (stop_grace_period / --stop-timeout) so in-flight renders can finish.
  • Settings: PORT (default 8080).

Fonts

The fonts are downloaded at image build time by the fonts stage of the Dockerfile, are not kept in git, and are licensed under the SIL Open Font License; see priv/static/fonts/LICENSES.md.

License

This project is licensed under the GNU Affero General Public License v3.0 (AGPL-3.0).

Copyright (C) 2025 Hocine HAMMOUCHE

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.