Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XtcZtnG4GQA1d6mW5PNrS9
4.6 KiB
Emil Shanayev — resume site
Live at https://shanaty.ru/resume/. A static Next.js site whose content is read live from Gitea, so it is updated by editing repositories, not by redeploying.
What comes from where
| On the page | Source |
|---|---|
| Name, role, bio, skills, experience, education, languages, contacts | data/resume.json (English) and data/resume.ru.json (Russian) in this repo (main branch) |
| Projects | Public repos of Emil_Shanaty on Gitea that carry the topic resume |
| The "full portfolio" link and its project count | The site's /portfolio/ page, which lists the public repos with the topic portfolio (button in the hero and a block after the project list; also in the top bar) |
| Project picture | Optional demo.png, demo.jpg, demo.gif or demo.webp in the root of that repo |
| Project title, kind, description, highlights, tech tags, links, order | Optional resume-card.json in the root of that repo |
A timer on the server (resume_sync.py in the auto repo) rebuilds https://shanaty.ru/data/resume.json every minute from these sources, so a change shows up within about a minute. If a source is broken (invalid JSON, missing fields, unsafe links) the previous published data stays and the problem is logged (journalctl -u gitea-resume).
Adding a project
- On Gitea, add the topic
resumeto the repository (it must be public). - Optional: put a
demo.png(or.jpg,.gif,.webp) in the repo root. Up to 6 MB, any proportions; animated GIFs and WebP keep animating. Without one the project gets a generated picture. - Optional: put a
resume-card.jsonin the repo root to control how the project reads:
{
"title": "Cortex Engine",
"kind": "AI-native 3D game engine",
"description": "One or two sentences on what it is and what makes it interesting.",
"highlights": ["Pure Vulkan 1.3 backend", "Playable FPS demo"],
"tech": ["C#", "Vulkan", "MCP"],
"links": [{ "label": "Docs", "href": "https://example.com/docs" }],
"rank": 1,
"hidden": false
}
Every field is optional. Without a card the page uses the Gitea description, the repo language and its topics (the topics resume, portfolio and featured are not shown as tags). rank puts a project first (lower first, unranked ones follow, newest first); "hidden": true removes it from the page. The same values can also live centrally in projectNotes (keyed by repo name) in data/resume.json; a resume-card.json wins over it.
The layout adapts to any number of projects: rows of two or three plates with a wider feature plate first, no lone project left in the last row, and a single project gets the whole width.
Languages
The site is bilingual and Russian is the default (the browser language is ignored). The RU/EN switch is in the top bar, the choice is stored in localStorage and is shared with the front page and the portfolio. Both languages are in the markup and html[data-lang] picks one, so there is no flash on load. data/resume.ru.json must keep the same structure as data/resume.json: skills, experience, education, languages, interests and contacts are paired by position, and anything missing in the Russian file falls back to the English text. Project names, descriptions and highlights come from the repositories (English) and are the same in both languages.
Editing the text
To point the portfolio button somewhere else (or rename it), add "portfolio": { "href": "https://...", "label": "All work" } to data/resume.json.
Edit data/resume.json (and its Russian twin data/resume.ru.json), commit and push to Gitea (git push gitea main). No rebuild or deploy is needed.
Development
npm install
npm run dev # http://localhost:3000, data from the public https://shanaty.ru/data/resume.json
npm run build # static export in out/
BASE_PATH sets the URL prefix (default /Emil_Shanayev_Resume, for GitHub Pages), NEXT_PUBLIC_DATA_URL the data URL and NEXT_PUBLIC_SITE_ORIGIN where the other pages of shanaty.ru live (empty in the production build, so the bar's links are root-relative). Fonts (Unbounded, Onest; SIL Open Font License) are bundled in app/fonts, so building needs no network.
Deploy
Automatic: every push to main (except changes that only touch data/** or *.md) runs .gitea/workflows/deploy.yml on the server's Gitea Actions runner. It builds the site and swaps the
resume/ folder of shanaty.ru in. Gitea runs .gitea/workflows instead of .github/workflows, so the GitHub Pages workflow (for the copy on GitHub) does not run on the server.
By hand, with the ssh key ~/.ssh/MyGitea: ./deploy-shanaty.sh.