MkDocs Material site that publishes four sections as one site at https://swe.springlee.dev, deployed on Cloudflare Pages.
Content sources live under content/:
| Section | Source | Tracked how |
|---|---|---|
| LeetCode Algorithms | content/leetcode-algorithms/ |
git submodule → leetcode-algorithms |
| SWE Interview | content/swe/ |
in this repo |
| System Design | content/system-design/ |
in this repo (vendored from liquidslr/system-design-notes) |
| Real Interview Questions | content/real-interview-questions/ |
in this repo |
Three of the four sections are edited here directly. Only LeetCode stays a separate repo, because it has its own GitBook publication, PR workflow and solution-generation tooling.
scripts/prepare.shcopiescontent/*intodocs/leetcode/,docs/swe/,docs/system-design/anddocs/real-interview-questions/, relocates GitBook assets to_assets/, and converts each source's GitBookSUMMARY.mdinto mkdocs-literate-nav format viascripts/convert_summary.py.docs/SUMMARY.mddefines the top-level tabs (Home / LeetCode Algorithms / SWE Interview / System Design / Real Interview Questions); each section's nav comes from its converted SUMMARY.sweandreal-interview-questionsare copied the same way — every*.mdexceptSUMMARY.mdand their ownCLAUDE.md— so maintenance docs stay out of the published site.system-designhas no SUMMARY.md, soscripts/gen_nav.pygenerates one from itsNN. Chapter Name/README.mdfolders, ordered by the numeric prefix and labelled with each chapter's H1. It also mixesReadme.mdandREADME.mdcasing;prepare.shnormalises it, because only an exactREADME.mdbecomes a directory index and the chapters' raw<img src="./images/...">tags resolve only from that index URL.scripts/leetcode_titles.pygives every LeetCodesolution.mdan<h1>naming its problem and cross-links it with the matchingdescription.md. Material's search titles a page by its first<h1>, so solutions opening with# Intuitionwere previously unreachable by problem number — searching "3876" found only the description, the SUMMARY page and the README index. It rewrites only the copies underdocs/, never the submodule.- Math: content uses GitBook-style inline
$$O(...)$$; rendered client-side by KaTeX auto-render (docs/javascripts/katex.js).
git clone --recurse-submodules https://github.com/software-engineer-learning/swe-site.git
# already cloned without it:
git submodule update --init --recursive
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
bash scripts/prepare.sh
.venv/bin/mkdocs serve # http://127.0.0.1:8000Edit content/swe/, content/system-design/ and
content/real-interview-questions/ in place, then re-run prepare.sh. Never edit
under docs/ — prepare.sh wipes those four directories on every run.
To pull new LeetCode solutions into your checkout:
git submodule update --remote content/leetcode-algorithmsYou rarely need to do this by hand: after a successful deploy, CI commits the
bumped pointer back to main itself, so the recorded pointer names the LeetCode
commit that is actually live. Pull before starting local work, or your next push
will be rejected as non-fast-forward.
Deploys are done by GitHub Actions (.github/workflows/deploy.yml), not by
Cloudflare's Git integration — do not also connect the repo in the Cloudflare
dashboard or every change would build twice. The workflow checks out submodules,
runs git submodule update --remote on the LeetCode submodule so the newest
solutions are published, builds the site, uploads it with wrangler pages deploy,
and then commits the bumped submodule pointer back to main. That bot commit
touches only content/leetcode-algorithms and carries [skip ci], and the push
trigger ignores that path — otherwise recording the pointer would kick off a
second, identical build of every LeetCode merge. It runs on:
- pushes to
mainof this repo — which now covers every edit to the SWE, System Design and Real Interview Questions sections, repository_dispatchevents of typecontent-updated, fired byleetcode-algorithms'gitbook.ymlworkflow on its content pushes,- manual runs (
workflow_dispatch).
content/system-design/ is a vendored copy of our fork of the third-party
liquidslr/system-design-notes.
Because it is vendored rather than cloned at build time, syncing the fork no longer
reaches the site on its own — pull upstream changes in by hand:
gh repo sync software-engineer-learning/system-design-notes --source liquidslr/system-design-notes
git clone --depth 1 https://github.com/software-engineer-learning/system-design-notes.git /tmp/sd
rsync -a --delete --exclude '.git/' --exclude '.github/' /tmp/sd/ content/system-design/
bash scripts/prepare.sh && .venv/bin/mkdocs build --strictReview the diff before committing — vendoring is what keeps third-party edits from landing on the site unreviewed.
-
Create the Pages project (Direct Upload):
npx wrangler pages project create swe-site --production-branch=main
(or Cloudflare dashboard → Workers & Pages → Create → Pages → Direct Upload.)
-
In this repo's GitHub settings → Secrets and variables → Actions, add:
CLOUDFLARE_ACCOUNT_ID— dashboard → Workers & Pages → right sidebar.CLOUDFLARE_API_TOKEN— dashboard → My Profile → API Tokens → Create Token → "Edit Cloudflare Workers"-style custom token with Account → Cloudflare Pages → Edit permission.
-
SITE_DISPATCH_TOKEN— a classic PAT with thereposcope on an account that can write tosoftware-engineer-learning/swe-site, held as an organization-level secret. Onlyleetcode-algorithmsstill needs it, since it is the one content repo left outside this one. Write access is what authorizesrepository_dispatch.Don't use a Cloudflare Pages deploy hook here. This project is Direct Upload, so a hook has no repo to clone and no build command to run — it re-serves the assets already uploaded and returns
success: truewhile the content stays stale. Deploy hooks only build on Git-connected Pages projects, and connecting this repo would make every change build twice. -
After the first deploy: Pages project → Custom domains → Add →
swe.springlee.dev. With thespringlee.devzone on Cloudflare, the CNAME and TLS are automatic.
- Put the markdown under
content/<section>/(or add a submodule there). - In
scripts/prepare.sh: add a<x>_srcvariable, an entry in the existence check, therm -rf/mkdir -pentries, a copy block, aconvert_summary.pycall writingdocs/<section>/SUMMARY.md, and the dir in the final page-countfindand thenormalize_fences.pycall. - Add the tab to
docs/SUMMARY.mdand a bullet todocs/index.md. - Add the section to the table above.