readProcessWithExitCode returns stdout/stderr as String, which forces GHC
to decode raw bytes using the current locale encoding. In a Docker
container with C locale (default), non-ASCII UTF-8 bytes (like 0xE2)
cause 'invalid argument' errors.
Replace with readProcessBytes which uses createProcess + hSetBinaryMode
to capture stdout/stderr as raw lazy ByteStrings, bypassing locale
decoding entirely. JSON is decoded directly from raw bytes via A.decode.
The urldecode helper in parseFormBody was using Html.urlDecode which only
handled %%20, %%23, and %%25. Full URLs encode colons, slashes, and dots
as %%3A, %%2F, and %%2E, so they were never decoded. The scraper received
the still-encoded URL and failed to fetch it.
Replace Html.urlDecode with a proper percent-decode that handles all %%XX
hex sequences using digitToInt.
Logs each stage of the import with [roux] prefix: request URL, script
path, exit codes, stderr output, JSON parse status, conversion status,
and file write path. Error messages now include the attempted URL for
easier debugging.
- Strip .cook from map keys to match lookupRecipe behavior
- Use makeRelative for event paths to match relative map keys
- Filter watcher to only .cook files
- Log watcher thread exceptions
- Use imported isSuffixOf from Data.List
- scripts/scrape-recipe: Python script using recipe-scrapers to extract
schema.org JSON-LD from a recipe URL (or local HTML file for testing)
- Dockerfile: add Python3 + recipe-scrapers in virtualenv, install the
scrape-recipe script at /usr/local/bin/scrape-recipe
- Dockerfile: production image for roux-server (Debian slim, tini, binary)
- docker-compose.yml: production compose with recipe/data volume mounts
- .gitea/workflows/deploy.yaml: Gitea Actions pipeline that builds, tests,
pushes Docker image, and deploys via SSH
Follows the same pattern used by the Atlas project.
Haskell\'s \' within double-quoted string literals is interpreted as
just \' (no-op escape). Switched to \\' to produce \' in the JS
output, which correctly escapes the single quotes inside the
single-quoted JS string literal for the CSS class name.
Installs a pre-commit hook that checks all staged .hs files are
formatted by fourmolu before allowing a commit. Configured via
core.hooksPath = .githooks.
Run: ./hs fourmolu -i <files> to format
- Rewrites indexPage to render a shell page with embedded JSON + JS
- Adds searchJs with vanilla JS for client-side list rendering, filtering, sorting
- Hash-based sort modes (#alpha, #tags, #course)
- Search filters by title and filename (case-insensitive)
- Fix: recipe links use /recipes/ prefix and encodeURIComponent for # encoding
- Fix: avoid double-render on initial page load
- Add nytToCooklang function to Roux.NYTimes
- buildMetadata: maps title, URL, description, time, servings
- buildSections: creates sections for ingredient groups then step groups
- ingredientGroupToSection: converts NYT ingredient items to
Cooklang step items with @ingredient references
- stepGroupToSection: converts method steps into Cooklang steps
- Add parseDuration, parseServings, parseNYTQuantity helpers
- Add extractIngredientName heuristic for core ingredient extraction
- Add 2 conversion tests verifying both example recipes
- All 32 tests passing, hlint clean