Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
30 changes: 30 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Spec 005 T3 — weekly dependency updates, grouped so each ecosystem opens one PR.
version: 2
updates:
- package-ecosystem: bundler
directory: /
schedule: { interval: weekly }
groups:
gems: { patterns: ["*"] }

- package-ecosystem: npm
directory: /
schedule: { interval: weekly }
groups:
root-packages: { patterns: ["*"] }

- package-ecosystem: npm
directory: /e2e
schedule: { interval: weekly }
groups:
e2e-packages: { patterns: ["*"] }

- package-ecosystem: github-actions
directory: /
schedule: { interval: weekly }
groups:
actions: { patterns: ["*"] }

- package-ecosystem: gitsubmodule
directory: /
schedule: { interval: weekly }
34 changes: 34 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Spec 005 T3 — static analysis of the site's JavaScript, Ruby plugins and workflows.
name: CodeQL

on:
push:
branches: [master]
pull_request:
branches: [master]
schedule:
- cron: '23 4 * * 1'

permissions:
contents: read

jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
permissions:
security-events: write
strategy:
fail-fast: false
matrix:
language: [actions, javascript-typescript, ruby]
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
build-mode: none
queries: security-and-quality
- uses: github/codeql-action/analyze@v3
with:
category: /language:${{ matrix.language }}
3 changes: 2 additions & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ staticman:
# Secret used to secure communication with Staticman
secret: "REPLACE_WITH_YOUR_SECRET"
# reCAPTCHA configuration
# Off until contribute (spec 003) replaces Staticman; the site key below is a placeholder.
recaptcha:
enabled: true
enabled: false
siteKey: "REPLACE_WITH_YOUR_SITE_KEY"
secret: "REPLACE_WITH_YOUR_SECRET"

Expand Down
18 changes: 2 additions & 16 deletions _includes/components/atoms/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -142,25 +142,11 @@
<script src="/javascript/ipl-theme.js" defer></script>

<!-- Legacy compatibility shim for pages not yet migrated to v1 (removal tracked in spec 001). -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3/dist/index.global.js" integrity="sha384-2ql948lIdLcGEE0/qxNiudyTjgauA3RDJERu5xW75kFCvSl5a9odyQYCb6tEjnmB" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer">
<style>
@media print {
.no-print { display: none !important; }
}
</style>

<!-- Analytics -->
{% if jekyll.environment == "production" %}
<script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date(); a = s.createElement(o),
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

ga('create', 'UA-XXXXXXXX-X', 'auto');
ga('send', 'pageview');
</script>
{% endif %}
17 changes: 2 additions & 15 deletions _includes/head.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<meta name="description" content="{{ page.description | default: site.description }}">

<!-- Tailwind CSS -->
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4.3.3/dist/index.global.js" integrity="sha384-2ql948lIdLcGEE0/qxNiudyTjgauA3RDJERu5xW75kFCvSl5a9odyQYCb6tEjnmB" crossorigin="anonymous"></script>

<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha512-iecdLmaskl7CVkqkXNQ/ZH/XLlvWZOJyj7Yy7tcenmpD1ypASozpmT/E0iPtmFIB46ZmdtAc9eNBvH0H/ZpiBw==" crossorigin="anonymous" referrerpolicy="no-referrer">

<!-- Custom Fonts -->
<style>
Expand All @@ -17,18 +17,5 @@
}
</style>

<!-- Analytics -->
{% if jekyll.environment == "production" %}
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');

ga('create', 'UA-XXXXXXXX-X', 'auto');
ga('send', 'pageview');
</script>
{% endif %}

<!-- HTMX para interatividade -->
<script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHCpAXeukg" crossorigin="anonymous"></script>
22 changes: 22 additions & 0 deletions docs/adr/0003-deploy-on-cloudflare-workers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 0003 — Deploy the site on Cloudflare Workers (static assets)

Date: 2026-10-11 · Status: Accepted · Spec: [005](../specs/005-website-modernization/spec.md)

## Context

The site is served through Cloudflare, configured outside the repository (`.cloudflare/pages.toml` is not a format
Cloudflare Pages reads), while `.github/workflows/deploy.yml` targets GitHub Pages. The `contribute` and `discuss`
services will run on Cloudflare Workers, and their sign-in sessions should be first-party cookies on the IPL domain.

## Decision

Build the Jekyll site in CI and deploy `_site` as **Workers static assets** with Wrangler (`wrangler.toml` in the
repository), keeping `_headers`, `_redirects` and the 404 page. The site Worker owns `ipl.better-web.org` and later
routes `/contribute/*` and `/discuss/*` to the services through service bindings. Preview deployments per pull request.
The migration happens **after** the modernization (spec 005), which removes the parallel build systems first.

## Consequences

- One platform and toolchain (Wrangler) for site and services; versioned, reviewable deploy configuration.
- First-party sessions for GitHub/ORCID sign-in; no cross-site cookies or wildcard CORS.
- The build moves to GitHub Actions; the GitHub Pages workflow and the Pages configuration are removed.
18 changes: 18 additions & 0 deletions docs/specs/005-website-modernization/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 005 — Plan

Spec: [spec.md](spec.md). Base branch: `chore/modernize` (stacked on `feat/design-v1`, which is not merged yet).

## Approach
Each phase is a PR, tests green at every step; deletions are listed and approved by the maintainer before running.

| Phase | Changes | Verification |
| --- | --- | --- |
| 1 Safety | Pin `@tailwindcss/browser@4.3.3` and Font Awesome 6.4.0 with SRI (`crossorigin="anonymous"`); remove GA snippets from both head includes; `.github/dependabot.yml`, CodeQL workflow; delete approved dead files; untrack `vendor/bundle` | New build tests for AC1/AC2; full suites |
| 2 Toolchain | Ruby 3.4 (`.ruby-version`, `Gemfile`, Docker/CI); `bundle update` gated by suites; consolidate scripts in `package.json`; port remaining Rake generation into plugins; CI workflow running Minitest + Playwright/axe; remove Rake/Make/`build.sh`/`deploy.yml` | Suites on CI; page-count parity before/after |
| 3 Front-end | Tailwind CLI v4 build step (`@theme` from `tailwind-theme.css`, content globs over templates and submodules) emitting `assets/css/site.css`; remove the runtime script; icon atom replaces Font Awesome per component | Screenshot diff of reference pages; axe |
| 4 Performance | Liquid profiling; move per-page menu/resource loops into plugin-generated data | Build time < 10 s |
| 5 Workers | `wrangler.toml` with `[assets] directory = "_site"`, `not_found_handling = "404-page"`; deploy job with PR previews | Smoke + E2E against the preview URL |

## Risks
- Tailwind compilation must scan submodule pages too, or legacy classes disappear (content globs include `en-US/**`, `pt-BR/**`).
- Removing Rake must not drop pages: parity test counts generated pages per language before/after.
32 changes: 32 additions & 0 deletions docs/specs/005-website-modernization/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 005 — Website modernization

Status: in progress · Tracking issue: [#27](https://github.com/InternetPublicLibrary/IPLibrary-website/issues/27) ·
ADR: [0003](../../adr/0003-deploy-on-cloudflare-workers.md)

## Why
The site builds and runs, but on an end-of-life Ruby, with 87 MB of vendored gems in git, three overlapping build
systems, a CI that does not run what developers run, unpinned third-party scripts and dead code. Moving to
Cloudflare Workers (ADR 0003) is only safe once the build is single, reproducible and tested.

## What
1. **Safety and hygiene:** third-party scripts pinned to exact versions with SRI; the dead Universal Analytics
snippet removed; `vendor/bundle` and debug/unused files out of git; Dependabot and CodeQL.
2. **One toolchain:** Ruby 3.4+; one set of `package.json` scripts calling Jekyll directly; Rake/Make/`build.sh`
retired after parity; CI runs `rake test`, Playwright + axe and internal link checks on every PR.
3. **Compiled front-end:** Tailwind compiled at build time from the IPL palette; no runtime CSS compiler; Font
Awesome replaced by the local icon atom.
4. **Performance:** full build under 10 s; Lighthouse budget on the reference pages.
5. **Deploy:** Workers static assets via Wrangler with PR previews (ADR 0003); GitHub Pages workflow removed.

## Acceptance criteria
- [ ] AC1 — No third-party `<script>`/`<link>` without an exact version and an `integrity` attribute (test).
- [ ] AC2 — No Google Analytics code in built pages (test).
- [ ] AC3 — git tracks no `vendor/bundle`, `node_modules`, `_site` or debug files; clone under 15 MB.
- [ ] AC4 — CI on every PR runs unit, E2E and accessibility suites and blocks merging on failure.
- [ ] AC5 — One documented command each for dev, build, test and deploy; Rakefile/Makefile/`build.sh` removed.
- [ ] AC6 — Built pages load no runtime CSS compiler; styles equal before/after (visual check on reference pages).
- [ ] AC7 — Full build < 10 s on CI hardware.
- [ ] AC8 — Production served by a Worker with static assets; PR previews available.

## Out of scope
Design changes (spec 001), content curation (spec 002), the contribute/discuss integrations (specs 003/004).
21 changes: 21 additions & 0 deletions docs/specs/005-website-modernization/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 005 — Tasks

## Phase 1 — Safety and hygiene
- [x] T1 Tests: third-party assets pinned with SRI (AC1); no Google Analytics in built pages (AC2) — RED first.
- [x] T2 Pin Tailwind browser 4.3.3 and Font Awesome 6.4.0 with integrity hashes; remove GA snippets.
- [x] T3 Dependabot (bundler, npm for root and `e2e/`, GitHub Actions) and CodeQL (JavaScript, Ruby).
- [ ] T4 Deletion list (vendor/bundle, debug files, unused JS/images, legacy includes) → maintainer approval → remove.
- [x] vendor/bundle untracked (approved).
- [ ] Debug files, legacy root includes, unused Semantic UI JS/images, unused components — awaiting approval.
- History rewrite declined: the clone keeps the existing pack; AC3's size target applies to the working tree.
## Phase 2 — Toolchain
- [ ] T5 Ruby 3.4; bundle update; suites green.
- [ ] T6 CI workflow (Minitest, Playwright + axe); remove npm-based workflows.
- [ ] T7 Single script set; Rake/Make/build.sh retired with page-count parity test.
## Phase 3 — Front-end
- [ ] T8 Tailwind CLI build; runtime script removed; visual check.
- [ ] T9 Icon atom replaces Font Awesome.
## Phase 4 — Performance
- [ ] T10 Build < 10 s.
## Phase 5 — Workers
- [ ] T11 wrangler.toml, deploy and preview jobs; remove GitHub Pages workflow and `.cloudflare/pages.toml`.
1 change: 1 addition & 0 deletions docs/specs/ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
| 002 Curated resources — `docs/specs/002-curated-resources/spec.md` on branch `content/curated-resources` | content data | in progress | — |
| [003 Contribute integration](003-contribute-integration/spec.md) | edits and suggestions UI | draft | `contribute` specs 002, 003 |
| [004 Discuss integration](004-discuss-integration/spec.md) | comments embed | draft | `discuss` specs 002–004 |
| [005 Website modernization](005-website-modernization/spec.md) | build, CI, assets, deploy on Workers | in progress | — (precedes the Workers deploy) |

Services (separate repositories, own specs): `InternetPublicLibrary/contribute` (hard fork of Staticman) and
`InternetPublicLibrary/discuss` (hard fork of giscus). Their API/embed contracts are defined in their repositories;
Expand Down
39 changes: 39 additions & 0 deletions test/third_party_assets_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require_relative 'test_helper'

# Spec 005 AC1/AC2: every third-party script or stylesheet is pinned to an exact version and
# protected by Subresource Integrity, and no page ships Google Analytics.
class ThirdPartyAssetsTest < Minitest::Test
EXTERNAL_TAG = %r{<(?:script\b[^>]*\bsrc|link\b[^>]*\bhref)="(?:https?:)?//[^"]+"[^>]*>}i
EXACT_VERSION = %r{[@/]v?\d+\.\d+\.\d+(?:[/"?#]|\z)}
ANALYTICS = /google-analytics\.com|googletagmanager\.com|\bgtag\(|\bga\(\s*'create'/

def built_pages
Dir.glob(File.join(SiteBuild.site.dest, '**', '*.html'))
end

def test_third_party_assets_are_pinned_with_integrity
offenders = Hash.new { |hash, key| hash[key] = [] }

built_pages.each do |file|
File.read(file).scan(EXTERNAL_TAG) do
tag = Regexp.last_match(0)
next if tag.start_with?('<link') && tag !~ /rel="(stylesheet|modulepreload)"/i

url = tag[/(?:src|href)="([^"]+)"/i, 1]
problem = if url !~ EXACT_VERSION then 'no exact version'
elsif tag !~ /\bintegrity="sha(256|384|512)-/ then 'no integrity'
end
offenders["#{url} (#{problem})"] << file.delete_prefix(SiteBuild.site.dest) if problem
end
end

assert_empty offenders.keys, offenders.map { |asset, pages| "#{asset}: #{pages.size} pages, e.g. #{pages.first}" }.join("\n")
end

def test_no_google_analytics_in_templates_or_pages
sources = Dir.glob(File.join(SiteBuild::ROOT, '{_layouts,_includes}', '**', '*.html'))
offenders = (sources + built_pages).select { |file| File.read(file).match?(ANALYTICS) }

assert_empty offenders.map { |file| file.delete_prefix(SiteBuild::ROOT).delete_prefix(SiteBuild.site.dest) }
end
end
29 changes: 0 additions & 29 deletions vendor/bundle/ruby/3.2.0/bin/ascii85

This file was deleted.

29 changes: 0 additions & 29 deletions vendor/bundle/ruby/3.2.0/bin/htmlproofer

This file was deleted.

29 changes: 0 additions & 29 deletions vendor/bundle/ruby/3.2.0/bin/jekyll

This file was deleted.

Loading
Loading