- HTML 43.5%
- CSS 25.5%
- Elixir 25.5%
- JavaScript 3.6%
- Dockerfile 1.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Release / publish-image (push) Successful in 3m50s
|
||
| .forgejo/workflows | ||
| config | ||
| lib | ||
| priv | ||
| .dockerignore | ||
| .formatter.exs | ||
| .gitignore | ||
| docker-compose.yml | ||
| Dockerfile | ||
| LICENSE | ||
| mix.exs | ||
| mix.lock | ||
| README.md | ||
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 incontrollers/page_html/.priv/content/<lang>.jsonis the content andpriv/static/the static files.- Dates in the content are ISO 8601 (
2023-09) orpresent.
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 achromium/chromium-headless-shellexecutable on thePATH) 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 return503and 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/.