Actually Static: When WordPress Stops Being the Enemy

Published: (February 8, 2026 at 08:28 AM EST)
13 min read
Source: Dev.to

Source: Dev.to

GitHub Copilot CLI Challenge – A WordPress‑to‑Static‑Site Workflow

This post is for developers who love writing in WordPress but want the speed and safety of static sites (Hugo / GitHub Pages).

The Journey

I spent years wrestling with WordPress performance and security:

  • Optimising caching layers
  • Hardening installations
  • Fighting plugin bloat

All to keep public‑facing sites running acceptably.

Then I discovered static site generators—Hugo, Astro—fast, secure, elegant.
But months of tweaking themes, debugging build pipelines, and fighting deployment workflows taught me something: I’d just traded one set of problems for another.

The Paradox

WordPressStatic Site Generators
Best writing environment ever built – mature UI, solid editor, focus on contentBest deployment target ever built – fast, secure, cheap to host, scalable by default

“I spent quite a lot of time tweaking Hugo sites instead of writing, and I’m afraid I’ll do the same thing if I transfer to Astro.” – @juliecodestack

The real problem isn’t WordPress or Hugo; it’s the friction between writing and deploying.

Why Public‑Facing WordPress Is a Burden

  • Requires decent hosting and heavy plugin dependencies (security, SEO, etc.)
  • Maintenance overhead is high
  • Performance is variable at best

I wanted a simpler solution: keep WordPress as a writing environment while completely removing its public presence.

Write normally. Publish automatically. No manual export, no scripts to run, no friction.

The Solution

A WordPress plugin that automatically syncs published posts to a GitHub repository, converts them to Markdown with Hugo‑compatible front‑matter, optimises images (WebP / AVIF), and triggers a GitHub Actions workflow for static‑site deployment.

Tech Stack

  • WordPress 6.9+ (PHP 8.1+)
  • GitHub API – atomic commits via the Trees API
  • Action Scheduler – async processing
  • Intervention Image – local image optimisation
  • Hugo – static site generator
  • GitHub Actions + GitHub Pages – CI/CD and hosting

Key Features

  1. Zero‑touch publishing – write in WordPress as usual.
  2. Automatic sync – on publish/update the plugin pushes a single commit to GitHub.
  3. Markdown conversion – each post becomes a Hugo‑compatible Markdown file with proper front‑matter.
  4. Image optimisation – originals are converted to WebP and AVIF before committing.
  5. CI deployment – a GitHub Actions workflow builds the site with Hugo and deploys it to GitHub Pages.

Result: Publishing in WordPress instantly puts a complete static version of the site online—no manual export, no extra steps.

Demo

  • WordPress admin (login: tester, password: Github~Challenge/2k26, Author rights) – see the result on the demo website.
  • In the site repository you can view the committed files and the workflow definition under .github/workflows.

Challenges & Lessons Learned

IssueWhat HappenedFix
Hugo Theme Version HellThe theme required Hugo 0.146.0, but the local install was 0.139.0 and GitHub Actions defaulted to 0.128.0.Pin the Hugo version explicitly in every environment; add debugging for TOML errors.
GitHub Pages URL StutteringDeployed site had broken internal links because baseURL didn’t match GitHub Pages expectations.Hard‑code the production URL in the workflow; accept minor navigation quirks in local previews.
Image Pipeline Memory LimitsProcessing 10+ images per post with AVIF exceeded PHP memory on shared hosting → fatal errors.Increase memory_limit to 512 M and batch‑process images sequentially instead of in parallel.
Action Scheduler Race ConditionsDuplicate commits occurred when a post was saved quickly (autosave, manual save, quick edit).Add debouncing, transient locks, and post‑meta flags to prevent redundant syncs.
PHP 8.1 StrictnessAn explode() on a null value froze the whole sync pipeline.Wrap risky calls in try‑catch‑finally to guarantee lock release and UI update.
Git Line‑Ending Hell (LF vs CRLF)Linux runners rejected files edited on Windows due to line‑ending mismatches.Enforce LF line endings globally via a .gitattributes file.

Closing Thoughts

Static sites can now be deployed in seconds on virtually any infrastructure. Keeping WordPress as a frontend editor makes sense only when you have a robust, automated deployment pipeline—exactly what this plugin provides.

This article is part of my “Farewell to WordPress” series, where I explore ways to move away from WordPress as a public‑facing platform while preserving its superb writing experience.


Happy writing, happy deploying!

Config File – Zero Cross‑Platform Headaches

The Partial Save Trap

WordPress tabbed interfaces only submit visible fields. When updating the Front Matter template, the GitHub PAT field wasn’t sent, so the value was overwritten with an empty string and the token was unintentionally deleted.

Fix

Use array_merge() (or a similar merge strategy) to preserve existing values during partial updates.

None of this was in the initial specifications. All of it was mandatory to ship.

What “48 hours” Actually Meant

TaskManual (estimated)With Copilot CLIActual Savings
Plugin boilerplate + WordPress standards~4 h20 min3 h 40 m
GitHub API integration (Trees, refs, commits)~6 h1.5 h4 h 30 m
Image optimisation pipeline~5 h2 h3 h
Async queue setup (Action Scheduler)~3 h45 min2 h 15 m
Admin UI + settings page~4 h1 h3 h
Hugo adapter (Markdown + front matter)~2 h30 min1 h 30 m
Debugging real‑world issues~8 h~8 h0 h (no AI help)
Total~32 h~14 h~18 h

Copilot accelerated the structured implementation. It did nothing for architectural decisions, debugging environment‑specific failures, or understanding WordPress.org compliance requirements.

Image Optimisation – Why It Matters

ScenarioFiles UploadedBuild TimeGitHub Actions Runner Minutes
Without local optimisation5 MB original JPEGs2‑3 min per postHigh (large files stored in repo)
With local optimisation (current approach)AVIF (50‑150 KB) + WebP (100‑300 KB) + original ≈ 500 KB total15‑30 sMinimal (just copy files)

Trade‑off: PHP memory limits & processing time on the WordPress side vs. the cost of GitHub Actions runner minutes. WordPress is idle 99 % of the time, so shifting the bottleneck to the free WordPress host is sensible.

What’s Currently Handled

  • Posts & Pages – Sync automatically with proper Hugo front matter
  • Deletions – Trashing a post/page in WordPress triggers file deletion in GitHub
  • Updates – Editing content re‑syncs, overwriting existing files
  • Categories & Tags – Converted to Hugo taxonomies in front matter
  • Featured Images – Optimised and linked in front matter (featured_image field)
  • Custom Fields – Basic fields map to front matter (extensible via adapter)

Current Limitations (MVP Scope)

  • ⚠️ Draft handling – Drafts stay in WordPress, never sync (intentional)
  • ⚠️ Revisions – Only published versions sync; revision history stays local
  • ⚠️ Complex blocks – Gutenberg blocks → HTML → basic Markdown (no advanced block preservation)
  • ⚠️ Shortcodes – Rendered to HTML before conversion (original shortcode lost)
  • ⚠️ ACF / Meta Boxes – Only standard custom fields supported (ACF needs a custom adapter)
  • ⚠️ Author pages – Not yet implemented (single‑author blogs work fine)

Deliberate Trade‑offs

  • Source of truth remains WordPress. The plugin does not sync bidirectionally; editing Markdown directly in GitHub will not flow back to WordPress. This keeps the system simple.

  • Theme changes & SSG migration – Thanks to the universal front‑matter template system:

    1. Update the front‑matter template in plugin settings (no code changes).

    2. Bulk re‑sync all posts via WP‑CLI:

      wp jamstack sync --all
    3. (Optional) Clean up the old file structure in Git if directory paths changed.

  • The adapter pattern is already in place. Adding support for Jekyll, Eleventy, Astro, etc., only requires a new adapter class; the core sync engine stays untouched.

  • What isn’t automated yet: Migrating between SSGs with fundamentally different content structures (e.g., Hugo’s content/posts/ vs. Astro’s src/content/blog/). Bulk file‑move operations in Git are currently manual.

  • Changing front‑matter conventions within the same SSG is now a simple settings change, not a refactoring project.

Repository Overview – atomic-jamstack-connector

  • WordPress plugin – WordPress.org compliant
  • GitHub API integration – Atomic commits
  • Asynchronous sync management – Action Scheduler
  • Hugo‑compatible Markdown generation
  • GitHub Actions workflow – Deployment

Process in Detail

  1. Writing – Use the standard WordPress editor; no changes to the writing experience.

  2. Automatic Commit – The GitHub repository receives Markdown, optimized images, and front‑matter.

    .github/workflows/
        └─ hugo.yml          ← GitHub Actions workflow
    content/                ← Generated Hugo content
    static/images/          ← Optimized assets
    (deployment status)    ← Last run status
  3. Hugo Structurecontent/posts/ is generated automatically with correct naming.

  4. Deployed Site – The static version is served via GitHub Pages, delivering optimal performance.

The whole thing forms a simple publishing chain: write in WordPress → publish → let the rest execute.

Key Features

1. Universal Front‑Matter Engine

Instead of hard‑coding for a single Hugo theme, we built a raw‑template system. Users define their own YAML (or TOML) with custom delimiters and placeholders such as {{id}}, {{title}}, {{image_avif}}.

This enables the plugin to adapt to any static‑site‑generator (SSG) convention:

  • Hugo – YAML front matter
  • Jekyll – custom taxonomy names
  • Eleventy – bespoke data structures

You control the output format; the plugin merely fills in the blanks.

2. Asset Management by WordPress ID

Optimised images (WebP & AVIF) are stored in folders named after the WordPress ID, e.g., static/images/1460/.

  • Rename your post slug for SEO – images never break.
  • The ID is immutable, guaranteeing permanent file paths.

3. Native WordPress Integration

  • First‑class citizen with its own sidebar menu and tabbed navigation.
  • Role‑based security:
    • Authors see only their own sync history.
    • Admins manage critical settings (e.g., GitHub PAT).

Responsible Cleanup

Clean Uninstall – removes all plugin traces (options and post meta) on uninstall, leaving zero database pollution.


4. Atomic Commits via GitHub Trees API

// Collect all files (Markdown + images)
$all_files = [
    'content/posts/2026-02-07-this-is-a-post.md'               => $markdown_content,
    'static/images/1447/featured.webp'                        => $webp_binary,
    'static/images/1447/featured.avif'                        => $avif_binary,
    'static/images/1447/wordpress-to-hugo-1024x587.webp'      => $webp_binary,
    'static/images/1447/wordpress-to-hugo-1024x587.avif'      => $avif_binary,
];

// Single atomic commit
$git_api->create_atomic_commit(
    $all_files,
    'Publish: This is a Post'
);

This approach is transactional: either everything commits or nothing does. No partial states, cleaner history.

5. WordPress‑Native Compliance

RequirementPreferred API
HTTP requestswp_remote_post() (instead of curl)
File handlingWP_Filesystem (instead of file_put_contents())
Database queries$wpdb prepared statements
Shell executionNever (exec(), shell_exec(), Git CLI are prohibited)

Following these guidelines makes the plugin suitable for publication in the official WordPress Plugin Repository.

Project Narrative

When I started this project I wasn’t looking for a tool to code for me; I wanted a way to accelerate execution of a project whose architecture was already clear.

Having already used GitHub Copilot CLI, Gemini CLI, and various LLMs on other projects, I knew these tools could produce code quickly—but without a precise framework they mainly generate code, not a coherent system.

Note: This isn’t the autocomplete in the editor, but a command‑line tool capable of generating complete files from structured prompts.


The Real First Step

  1. Write specifications – define scope and break the project into functional blocks.
  2. Identify non‑negotiable constraints – WordPress‑native only, no shell execution, reliable async processing, atomic GitHub commits, WordPress.org compliance.
  3. Organize development into successive stages.

This mirrors what a technical project manager does before handing implementation to a team. The difference here is that the “team” is a tool that can produce code very quickly—if the instructions are clear and precise.


My Workflow

PhaseAction
SpecificationWrite functional & technical specs, prompts, refined instructions.
GenerationLet Copilot generate code.
VerificationReview output against constraints.
CorrectionAdjust prompts, fix issues.
IterationRepeat until the desired result is achieved.

The pattern that emerged was:

specification → generation → verification → correction → iteration.


Observations

  • Copilot can structure an entire class in seconds, propose a coherent implementation, or refactor a block.
  • It can also forget an essential hook, overwrite an existing method, or produce code that fails to comply with the initial constraints.

Real Examples of Issues

  • Method replaced by an incomplete stub.
  • Hook not registered → silent failures.
  • File generated but not actually written to disk.
  • Fatal error on activation (common in strict WordPress environments).

Each incident required returning to fundamentals: verify → understand → correct → reformulate.


Key Takeaways

  • Using Copilot effectively is not “write one prompt and wait”.
  • It’s a continuous piloting process where the quality of instructions directly determines the quality of output.
  • The tool shines for structured tasks: class creation, file organization, repetitive function implementation, refactoring, documentation.
  • Responsibility for architecture, technical choices, and overall coherence remains entirely human.

Project Outcome

  • Built in less than two days – not because the tool replaced design work, but because once design was complete, execution accelerated dramatically.
  • GitHub Copilot CLI proved most valuable as an accelerator for an already thought‑out and structured project, not a substitute for development.

What Worked Well

  • Clear, detailed specifications.
  • Iterative verification and correction.

What Required Constant Supervision

  • Ensuring WordPress‑native compliance (no shell commands, proper APIs).
  • Maintaining hooks and activation routines.
  • Verifying that generated files are actually written to disk.

Final Thoughts

GitHub Copilot CLI didn’t replace development; it didn’t eliminate the need to think, architect, or decide. Used as an execution partner rather than an automatic generator, it allowed a clear idea to become a functional system quickly.

That’s where these tools truly make sense: they don’t change how we build, but they reduce the distance between imagination and production.

In this specific case they solved a real tension: write comfortably in WordPress while publishing to a high‑performance static site—no friction, no compromises, just a workflow that works.


Further Reading

If you’re interested in other approaches to moving away from WordPress, check out my “Farewell to WordPress” series, where I explore migrations to Hugo and Astro with different strategies and trade‑offs.

0 views
Back to Blog

Related posts

Read more »

E2E Tests: The Full Stack Check

Part of The Coercion Sagahttps://dev.to/nicolas_vbgh/programming-by-coercion-b5 — making AI write quality code. Backend tests pass. Frontend tests pass. The con...