This guide is for contributors who want to understand the codebase, make changes, or help maintain the project.
Architecture overview¶
How it works¶
Templates: Jinja2 templates render the initial HTML structure for the profile selection form
React App: JavaScript/React code handles the interactive UI, form state, and dynamic features
Webpack: Bundles the frontend code and outputs it to
static/HTTP Handlers: JupyterHub serves the static assets when the profile page loads
KubeSpawner Integration: The
setup_ui()function configures KubeSpawner to use these templates and handlers
Design philosophy¶
Keep this tool a fairly simple React app focused on profile selection. This won’t become a super-heavy, complex application.
Why react?¶
If this file gets over 200 lines of code long (not counting docs / comments), start using a framework
From the BinderHub JS Source Code
The file did get more than 200 lines long, and BinderHub learned this lesson the hard way. For this project:
Lightweight: Plain React without TypeScript keeps it approachable
Mainstream: Attracts frontend developers and contributors
Just Right: Complex enough for multiple interactive features, not so heavy that it’s hard to maintain
Single Page: Perfect scope for React—one complex page with state management
Development setup¶
Setting up minikube¶
Currently, these instructions work with minikube but can be adapted to any local Kubernetes setup.
Download, set up and start minikube
Allow spawned JupyterHub server pods to talk to the JupyterHub instance on your local machine:
# Linux sudo ip route add $(kubectl get node minikube -o jsonpath="{.spec.podCIDR}") via $(minikube ip) # macOS sudo route -n add -net $(kubectl get node minikube -o jsonpath="{.spec.podCIDR}") $(minikube ip)You can later undo this with:
# Linux sudo ip route del $(kubectl get node minikube -o jsonpath="{.spec.podCIDR}") # macOS sudo route delete -net $(kubectl get node minikube -o jsonpath="{.spec.podCIDR}")
Setting up the development environment¶
Clone the repository:
git clone https://github.com/2i2c-org/jupyterhub-fancy-profiles.git cd jupyterhub-fancy-profilesSet up a virtual environment (using
venv,conda, etc.)Install Python dependencies:
pip install -r dev-requirements.txt pip install -e .This also builds the JS and CSS assets.
Install configurable
-http -proxy (required for JupyterHub): npm install configurable-http-proxyAdd
configurable-http-proxyto your$PATH:export PATH="$(pwd)/node_modules/.bin:${PATH}"Start JupyterHub and navigate to
localhost:8000:jupyterhubYou can login with any username and password.
If working on JS/CSS, run this in another terminal to automatically watch and rebuild:
npm run webpack:watch
Testing¶
Tests for the frontend use Jest and React Testing Library for rendering components and asserting DOM state.
Run all tests¶
npm testRun specific test suite¶
To run tests in a specific file (e.g., ProfileForm.test.tsx):
npm test ProfileFormMaking a release¶
We use automation to publish releases to PyPI. Release early and often!
Creating the release¶
Update your local checkout:
git checkout main git stash # if needed git pull upstream main # or origin, as neededCreate a new git tag:
git tag -a v<version-number>In the tag message, at minimum write:
Version <version-number>Ideally, include a brief changelog of notable changes.
Push your tag to GitHub:
git push origin --tagsDone! A new release will automatically be published to PyPI.
Generating release notes¶
After making the release:
Install
github-activity:pip install github-activityGenerate release notes using the previous and current release tags:
github-activity 2i2c-org/jupyterhub-fancy-profiles -s <last-release-tag> -u <this-release-tag>For example, for v0.5.0:
github-activity 2i2c-org/jupyterhub-fancy-profiles -s v0.4.0 -u v0.5.0Copy the output and rearrange/categorize as needed.
github-activitywill automatically group PRs based on tags (e.g.,enhancement,bug) or prefixes (e.g.,[ENH],[BUG]).Create a GitHub release, use the tag as the title, and paste in the generated release notes.
Click Publish Release.