No description
  • Python 82.7%
  • Astro 7.3%
  • Jinja 4.1%
  • TypeScript 3.8%
  • Just 1.8%
  • Other 0.2%
Find a file
giannifer7 306aa62e41 Sync the preview iframe's theme when toggled while a preview is open
Found during a real visual walkthrough (not just tests): toggling
light/dark after already having a preview open left the iframe on
whichever theme it happened to load with, since it only reads
localStorage once, at load time. Since the iframe is same-origin, the
toggle can just flip its data-theme attribute directly, instantly -- no
reload or re-fetch needed.
2026-07-20 20:24:17 +02:00
.claude/skills/verify Document and test ra_upload, fix locale/wp_options/theme-toggle smells 2026-07-10 21:51:27 +02:00
.deepseek/state Add progress counters and resumable upload for archive.org 2026-07-06 16:04:39 +02:00
docs Document the new preview feature and its tabo-side infrastructure 2026-07-20 11:29:55 +02:00
editor Sync the preview iframe's theme when toggled while a preview is open 2026-07-20 20:24:17 +02:00
meta Turn meta/ into a proper uv project matching the other three packages 2026-07-20 10:26:25 +02:00
migration Fix pylint's max-module-lines config and split files that were over 300 lines 2026-07-20 09:59:01 +02:00
scripts Fix pylint's max-module-lines config and split files that were over 300 lines 2026-07-20 09:59:01 +02:00
site Allow the public hostname through Vite's dev-server host check 2026-07-20 11:16:55 +02:00
.env.example Add ra-editor: minimal custom post/page editor, replacing Decap/Sveltia 2026-07-18 19:34:36 +02:00
.gitignore Stop serving a 344 KB 512x512 PNG as a 36px header icon 2026-07-18 11:22:34 +02:00
.mise.toml Phase 4-5: static export pipeline, Astro site, podcast UI overhaul, image optimization, site icon, mobile-first responsive 2026-07-05 20:43:49 +02:00
.python-version Initial migration tooling 2026-07-02 13:39:47 +02:00
config.toml.example Fix two real preview bugs: stale iframe on same-day previews, 404 race 2026-07-20 17:04:32 +02:00
justfile Turn meta/ into a proper uv project matching the other three packages 2026-07-20 10:26:25 +02:00
README.md Document and test ra_upload, fix locale/wp_options/theme-toggle smells 2026-07-10 21:51:27 +02:00

Restiamo Animali Migration

Offline tooling and planning for migrating the Restiamo Animali WordPress site to a static site.

Documents

  • docs/plan.md: actionable migration plan and phase gates.
  • docs/instructions.md: original migration instructions and constraints.
  • docs/content-contract.md: versioned static export contract.
  • docs/chatgpt-handoff.md: shareable project status summary.

Generated reports live outside the repository:

  • /mnt/data/restiamoanimali/reports/audit.md: generated WordPress export audit.

Migration Tooling

The migration package lives in migration/ and is Python 3.14-only.

Tool Roles

  • mise installs and selects the outer command-line tools declared in .mise.toml: Python, uv, and just.
  • uv manages the Python package environment in migration/, using migration/pyproject.toml and migration/uv.lock.
  • uv.lock pins Python development tools such as ruff, mypy, pylint, and pytest.
  • just is the task runner. The root justfile contains the common project commands, runs them from the correct directory, and loads .env via its built-in dotenv-load setting.
  • scripts/ is a small Python 3.14 uv project for operational helpers. The FTP recipes call its ra-ftp command, which validates environment variables and runs lftp.

After mise activate is set up in your shell (one-time), just, python, and uv resolve to the mise-pinned versions automatically and just loads .env itself. The mise exec -- prefix is only needed during bootstrap before activation is in place. Do not call a globally installed ruff, mypy, pylint, or pytest directly for this project.

lftp (>= 4.8.0) is required for FTP backup commands. Install it with your system package manager. The ftp-check recipe verifies the version. mariadb-dump or mysqldump is required for database export. On Arch-based systems, install it with paru -S mariadb-clients or the equivalent package manager command.

Bootstrap

From a fresh clone:

cd restiamoanimali
mise trust
mise install
cp .env.example .env
mise exec -- just sync
mise exec -- just doctor
mise exec -- just check

What these commands do:

  • mise trust allows mise to evaluate this repository's .mise.toml, including the local .env loader.
  • mise install installs the pinned Python, uv, and just versions if they are not already available.
  • cp .env.example .env creates the local credential file. Fill it only when you need FTP access or other machine-local secrets.
  • mise exec -- just sync creates or updates migration/.venv from migration/uv.lock and scripts/.venv from scripts/uv.lock.
  • mise exec -- just doctor prints the resolved versions of Python, uv, just, ruff, mypy, pylint, and pytest.
  • mise exec -- just check runs formatting checks, linting, type checking, pylint, and tests.

Local credentials (.env)

Use .env for local credentials such as FTP_HOST, FTP_USER, and FTP_PASSWORD. The justfile has dotenv-load enabled, so normal just ... commands read .env directly. The [env] section in .mise.toml also points to .env, so bootstrap commands that still use mise exec -- just ... inherit the same variables.

The real .env is listed in .gitignore and must never be committed; keep only .env.example in Git.

Tool configuration (config.toml)

config.toml is the local source of truth for non-secret project settings: local data paths, the default WordPress export path, remote WordPress paths, FTP transfer tuning, and optional audio upload destinations ([[ra_upload.ftp]], [[ra_upload.scp]]; see config.toml.example). The real config.toml is listed in .gitignore so it stays local and is never committed; keep only config.toml.example in Git.

From a fresh clone:

cp config.toml.example config.toml

For the current hosting layout, wordpress.remote_site_root is /www.restiamoanimali.it, while wordpress.remote_wordpress_root includes the blog subdirectory: /www.restiamoanimali.it/blog.

If the FTP server presents a legacy certificate whose hostname does not match the host in .env, set ftp.ssl_verify = false locally in config.toml. If listings or transfers stall after login, plain FTP is usually the reliable fallback for this legacy Aruba hosting; the example config already uses that.

Configuration is resolved in this order (highest priority wins):

  1. --data-root CLI flag
  2. --config path/to/config.toml CLI flag
  3. RA_DATA_ROOT environment variable
  4. config.toml at the repository root
  5. Built-in default: /mnt/data/restiamoanimali

When --config is used, repository config.toml discovery is skipped.

Run tests:

just test

Format code:

just format

Run the full maintenance suite:

just check

Generate the current audit:

just audit

Generate the first static export artifacts:

just export

This writes /mnt/data/restiamoanimali/export/ with versioned JSON sidecars, Markdown content, copied WXR attachment and local audio files, and reports/export-blockers.md. The strict recipe exits non-zero when blockers are present. To deliberately write a snapshot while keeping the blockers for inspection:

just export-allow-blockers

Verify the generated export before building a static site:

just verify-export

Generate a recovery map from original WordPress audio filenames to normalized export filenames:

just audio-map

This writes /mnt/data/restiamoanimali/reports/audio-filename-map.csv and /mnt/data/restiamoanimali/reports/audio-filename-map.md.

Build the Astro static site from the verified export:

just site-build

site-build leaves /media/... as external references. This avoids copying the multi-gigabyte media archive into site/dist/; deployment should publish or alias /mnt/data/restiamoanimali/export/media at /media.

Run the local Astro development server:

just site-dev

site-dev creates a local site/public/media symlink to the exported media tree so images and audio work while browsing the development server.

Run the same server on the local network for real phone/tablet testing:

just site-dev-lan

Open http://<computer-lan-ip>:4321/ on a device connected to the same hotspot or Wi-Fi network. Find the computer address with ip addr and use the address on the active Wi-Fi/hotspot interface, usually 192.168.x.x or 10.x.x.x. This exposes the development server only on the local network; use the VPS only for shareable previews or deployment testing.

Check FTP credentials and remote paths:

just ftp-check
just ftp-ls
just ftp-ls-path /www.restiamoanimali.it/blog/wp-content/uploads/audio

Mirror WordPress files from FTP into the local backup area:

just mirror-wp-content
just mirror-wordpress-root

The mirror-wp-content recipe downloads wordpress.remote_wp_content to paths.data_root/backup/wp-content. The mirror-wordpress-root recipe downloads the WordPress installation root to paths.data_root/backup/wordpress-root, excluding wp-content so the large media/plugin/theme tree is mirrored by the dedicated resumable recipe. These backup directories are outside Git because they are large, reproducible, and may contain secrets such as wp-config.php.

The FTP orchestration lives in the Python scripts/ project, but transfers are still delegated to lftp. That keeps recursive mirroring, resume support, parallel transfers, retry behavior, timestamp handling, and old FTP server compatibility in a mature external tool. Python's standard ftplib and curl are useful for individual FTP operations, but they do not provide a complete resumable directory mirror by themselves.

Dump the WordPress database:

just database-check
just database-dump

This reads database connection details from paths.data_root/backup/wordpress-root/wp-config.php and writes paths.data_root/backup/database.sql. The database password is passed through a temporary MySQL defaults file, not through command-line arguments or Git.

Generate the shareable WordPress environment inventory:

just inventory-backup

This writes paths.data_root/backup/environment.md and paths.data_root/backup/wp-config.redacted.php. The inventory records the WordPress version, active theme, installed themes, and installed plugins from the local backup. The redacted config removes database credentials and salts. The PHP runtime version is not recoverable from the static backup and must be recorded from the hosting panel or WordPress admin when needed.

Update backup metadata:

just metadata-backup

This writes paths.data_root/backup/metadata.md with the configured paths, snapshot sizes, file counts, audio count, checksum target count, commands used, and known missing backup items. It does not include FTP credentials.

Update deterministic backup checksums:

just checksum-backup

This writes paths.data_root/backup/SHA256SUMS from the configured backup root. The checksum file itself is excluded from the generated list. Run metadata-backup before checksum-backup when both are needed, so metadata.md is included in the checksum snapshot.

Remove temporary FTP artifacts only when they are no longer needed:

just ftp-rm '/www.restiamoanimali.it/blog/wp-content.zip.*'

ftp-rm is destructive on the production server. It refuses patterns that would match the whole WordPress root or wp-content, but it still passes the given pattern to FTP after that guard.

Upload audio files to configured destinations:

just audio-upload-archive
just audio-upload-ftp
just audio-upload-scp
just audio-upload-all

These read export/data/audio.json and export/data/posts.json (run just export first) and upload each MP3 via scripts/src/ra_scripts/ra_upload. audio-upload-archive uploads to archive.org (rate-limited to ~2/minute, resumable — already-uploaded items are skipped). audio-upload-ftp and audio-upload-scp use destinations configured under [[ra_upload.ftp]] / [[ra_upload.scp]] in config.toml; an unconfigured destination is skipped with a log message, not an error. audio-upload-all runs archive + ftp + scp. Dropbox upload exists but has no just recipe and needs DROPBOX_TOKEN in .env; run it directly with uv run ra-upload dropbox from scripts/.

Background variants that log to /tmp and detach:

just audio-upload-archive-bg
just audio-upload-scp-bg

Push to the configured forge remote:

just push

The push recipe uses $GIT_SSH_COMMAND when it is already set. Otherwise it defaults to ssh -F $HOME/.ssh/config, which keeps the SSH configuration machine-local.

The default audit input is /mnt/data/restiamoanimali/backup/restiamoanimali.WordPress.2026-07-02.xml. The default generated report is /mnt/data/restiamoanimali/reports/audit.md. Generated reports and backup data intentionally live outside the repository.