Skip to content

Modernize HFLOSSK into a Flask 3.0 site deployed to GitHub Pages#1

Open
justwheel wants to merge 3 commits intomainfrom
2026/stack-update
Open

Modernize HFLOSSK into a Flask 3.0 site deployed to GitHub Pages#1
justwheel wants to merge 3 commits intomainfrom
2026/stack-update

Conversation

@justwheel
Copy link
Member

@justwheel justwheel commented Mar 15, 2026

Summary

This PR modernizes the HFLOSSK course website stack, inspired by a recent message from @decause that reminded me this project deserves to be buildable and deployable again. The site had been untouched since ~2014, with a defunct CI pipeline (Travis CI), Python 2.7 dependencies, and deployment tied to OpenShift 2 (which no longer exists). The goal was to get a passing CI pipeline, a working static site build, and a path to production deployment via GitHub Pages.

What changed

  • Python packaging: Replaced setup.py and distribute_setup.py with modern pyproject.toml (PEP 621). Updated all dependencies to current versions.
  • Template engine: Migrated all 37 Mako templates (.mak) to Jinja2 (.html). Flask-Mako was unmaintained since 2015 and incompatible with Flask 3.x. Jinja2 is Flask's native template engine, eliminating two dependencies.
  • Flask 3.x compatibility: Fixed deprecated flask.ext.* imports, duplicate blueprint registration (requires name= in Flask 3.x), yaml.safe_load(), and Python 3 string encoding.
  • CI/CD: Replaced Travis CI with GitHub Actions running in Fedora minimal containers on Python 3.14. Lint with ruff, test with pytest, and freeze the site with Frozen-Flask for every push.
  • Static site generation: Added freeze.py using Frozen-Flask to generate a complete static HTML site from the Flask app. Non-main branches upload a preview artifact. Production deploys to Cloudflare Pages.
  • Documentation: Added CLAUDE.md for AI-assisted development guidance and rewrote README.md with modern setup instructions for Fedora Linux 43. See 📝 Docs: Add CLAUDE.md for AI-assisted development guidance #2
  • Cleanup: Removed Tornado/OpenShift deployment code, Python 2 compatibility code, and all legacy Mako template files.

Why a static site?

The HFLOSSK site is fundamentally static content — course materials, lecture notes, homework assignments, and student profile pages all driven by YAML data and templates. The only dynamic feature was live RSS blog post counting, which isn't needed for a preserved historical site. Frozen-Flask lets us generate the entire site as static HTML and host it for free on Cloudflare Pages (or GitHub Pages), with no server to maintain.

Test plan

  • CI pipeline passes (lint + tests)
  • Preview build freezes the site successfully (246 files)
  • Cloudflare Pages deploy preview renders correctly (this should start working after this PR is merged, then future PRs should get a Cloudflare Pages deploy preview)
  • Visual spot-check of key pages (home, syllabus, participants, lectures)

@justwheel justwheel added documentation Improvements or additions to documentation improvement Improves on something that already exists quality assurance CI tests, unit tests, integration tests, any kind of testing labels Mar 15, 2026
@justwheel
Copy link
Member Author

Whenever this is merged, please use a rebase & merge strategy instead of squashing. The commits are broken up into smaller, thematic changes, so they can be kept as-is in the commit log.

Here is a screenshot from a local rendered preview:

Screenshot of the 2014 HFOSS@RIT course homepage, displaying class schedule and location details, a main title banner for the Humanitarian Free/Open Source Software Development Course, and brief introductory sections for the Bootstrap, Flask, and Mako frameworks.

@justwheel justwheel force-pushed the 2026/stack-update branch 2 times, most recently from 7f0e1d8 to cc9fd78 Compare March 15, 2026 10:06
@MoralCode
Copy link

I see these commits were co-authored by an AI tool. Can you share more about your AI workflow (i.e. what role/involvement did the AI tool have? how you reviewed the changes to ensure there were no unintended changes to the code that you didnt ask for? Did you make any corrections to the output as a result of review?)

Im also skeptical about the need to introduce an AGENTS.md file here. Maybe we can split that change into a new PR for independent discussion.

Copy link

@MoralCode MoralCode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good first pass, but still needs some changes (especially with making sure the README is accurate for how the code currently works)

@justwheel
Copy link
Member Author

@MoralCode wrote…
I see these commits were co-authored by an AI tool. Can you share more about your AI workflow (i.e. what role/involvement did the AI tool have? how you reviewed the changes to ensure there were no unintended changes to the code that you didnt ask for? Did you make any corrections to the output as a result of review?)

I looked at this project with a few, clearly-defined goals in mind. I wanted to accomplish two things: an updated stack using a newer version of Python and Flask, and a static site deployment to GitHub Pages. With these two goals in mind, I used Claude Code to evaluate the current state of the code, iteratively debug with me in real-time as I tested whether I could get the site to run locally.

After creating the GitHub Actions CI check, this provided fast feedback to me about whether the CI environment was able to both pass a lint check and pytest check. The preview step also uses Frozen-Flask and freeze.py to build the static site from the existing Flask site. At first, the lint check and pytest checks failed when Mako was being used. I was hoping to avoid migrating from Mako to something else, but using Mako in Flask 3.x+ is difficult without hacky workarounds. Since so much work is done in this PR already, I decided to avoid hacky workarounds and convert the Mako templates to Jinja2 HTML templates instead. This fully brings them into the present way of how Flask is best designed to work. Hopefully, this change will afford us some more years of trouble-free maintenance with the current stack.

As Claude Code did its thing, I opted to manually approve all of the code changes it made. I did it this way because it offered me a chance to both understand what changes were being made and why, and to interrogate the change further before it was made, if something seemed incorrect or wrong to me. Therefore, the final output I received from Claude Code was rarely edited further by me. However, the important detail is that I am manually reviewing all of the changes made and offering feedback in real-time, as part of the review process I follow.

On the topic of my review process, you will note that all of my git commits are cryptographically signed with my GPG key. I do not share my private key or passphrase with the AI agent. It cannot make GPG-signed git commits on my behalf. Therefore, each time I am making a git commit, it is a human-initiated process, by me, where I have reviewed all of the changes in the commit. I write the git commit messages to have a purpose of explaining what the changes are and why I am making them.

I believe the git commit log is also a transparent ledger of the changes made, the process followed, and how these outputs were verified.

I hope these answers address your questions about how I used AI in this Pull Request.

@MoralCode wrote…
Im also skeptical about the need to introduce an AGENTS.md file here. Maybe we can split that change into a new PR for independent discussion.

I answered this in the in-line code review above.

Replace the 2013-era build system with modern Python packaging and fix
all compatibility issues for Flask 3.x on Python 3.14.

Packaging
---------
- Replace setup.py and distribute_setup.py with pyproject.toml (PEP 621)
- Update dependencies: Flask 3.x, PyYAML 6.x, feedparser 6.x
- Replace tornado with gunicorn for production serving
- Replace nose/flake8 with pytest/ruff for testing

Flask 3.x and Python 3 fixes
----------------------------
- Replace removed flask.ext.mako imports with flask_mako
- Replace unsafe yaml.load() with yaml.safe_load()
- Remove Python 2 __future__ imports
- Fix gravatar email encoding order for Python 3 unicode strings
- Simplify app.py by removing defunct Tornado/OpenShift code
- Fix ruff import sorting, exclude legacy scripts/ and ipynb/
- Skip YAML files at unexpected directory depth in participants

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Justin Wheeler <git@jwheel.org>
Replace the defunct Travis CI pipeline with GitHub Actions running in
Fedora minimal containers.

- CI runs lint (`ruff`) and tests (`pytest`) on every push and PR using
  `fedora-minimal:43` with the default Python 3.14 runtime
- Preview builds freeze the site into static HTML and upload it as a
  downloadable artifact for non-`main` branches (retained 7 days)
- Deploy workflow generates the static site via Frozen-Flask and
  publishes to GitHub Pages on push to `main`
- Add `freeze.py` to enumerate all freezable routes while excluding
  dynamic endpoints like `/blog/<username>` that parse live RSS feeds
- Use `actions/checkout@v6`, `ruff --fix` for environment-portable
  import sorting
- Add modern `README.md` with Fedora Linux 43 setup instructions,
  replacing the outdated `README.rst` (Python 2, `yum`, OpenShift 2
  references, etc.)

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Justin Wheeler <git@jwheel.org>
Replace the unmaintained `flask-mako` package (last updated 2015,
incompatible with Flask 3.x) with Jinja2, Flask's built-in template
engine. This eliminates two dependencies and removes the last blocker
for running on modern Flask.

- Convert all 37 `.mak` templates to `.html` Jinja2 equivalents
- `master.html`: `<%def>` blocks → `{% block %}`, `${self.body()}` →
  `{% block body %}`
- `blogs.html`: Inline Python moved to view, `loop.index` adjusted for
  Jinja2's 1-based indexing, `${var|h}` → `{{ var|e }}`
- `syllabus.html`: block overrides, unused macro removed
- Update `site.py`, `blueprints.py`, `participants.py` to use Flask's
  native `render_template`
- Add `name=` to duplicate blueprint registrations (Flask 3.x)
- Update `freeze.py` to use `(endpoint, values)` tuples for namespaced
  blueprint endpoints
- Remove `pyproject.toml` dependencies on `Mako` and `flask-mako`
- Delete all 37 old `.mak` template files

Assisted-by: Claude Opus 4.6 (1M context)
Signed-off-by: Justin Wheeler <git@jwheel.org>
@justwheel
Copy link
Member Author

The AI contribution guidelines were moved over to PR #2. This PR is strictly the GitHub Actions CI pipeline and the Flask 3.x modernization work.

@justwheel
Copy link
Member Author

@MoralCode Mind looking again now that the CLAUDE.md file is pulled out?

@MoralCode
Copy link

I noticed when running this that it requires python 3.14 and no lower. Not likely a problem, but just an observation

Screenshot:
Screenshot_20260321_225425

are the quotes on the homepage intentional or just part of the template?

is the participants page intended to be empty?

Should we update the IRC link in the navbar to point to libera, not freenode to match similar changes made to the main fossrit website?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation improvement Improves on something that already exists quality assurance CI tests, unit tests, integration tests, any kind of testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants