- PHP 100%
| includes | ||
| .distignore | ||
| .gitignore | ||
| CHANGELOG.md | ||
| eg-forgejo-updater.php | ||
| index.php | ||
| LICENSE | ||
| README.it-IT.md | ||
| README.md | ||
| readme.txt | ||
| uninstall.php | ||
EG Forgejo Updater
🇬🇧 English · 🇮🇹 Italiano
Minimal WordPress plugin that automatically updates plugins hosted on a self-hosted Forgejo instance.
A lightweight replacement for Git Updater: no ad banners, no Redis compatibility issues, token saved in wp-config.php once and for all.
Project structure
eg-forgejo-updater/
├── eg-forgejo-updater.php # Bootstrap, plugin header, class includes
├── uninstall.php # Data cleanup on uninstall
├── index.php # Directory listing protection
├── readme.txt # Standard WordPress readme
├── CHANGELOG.md # Changelog
├── LICENSE # GPL-2.0-or-later
└── includes/
├── class-eg-forgejo-api.php # Forgejo REST API client
├── class-eg-forgejo-updater.php # WordPress hooks for update detection and install
├── class-eg-forgejo-admin.php # Admin page (Plugins submenu)
└── index.php # Directory listing protection
Requirements
- WordPress 6.0 or later
- PHP 8.0 or later (uses typed properties and named arguments)
- Forgejo instance reachable over HTTPS from the WordPress host
- For each managed plugin: at least one tag or release in the corresponding Forgejo repo
Installation
- Upload the
eg-forgejo-updater/folder towp-content/plugins/ - Add
FORGEJO_ACCESS_TOKENtowp-config.php(required for private repos) - Activate the plugin from Dashboard > Plugins
- Plugins with a
Forgejo Plugin URIorGitea Plugin URIheader are detected automatically - To install additional plugins from Forgejo, use the built-in feature described below
Configuration in wp-config.php
Add these lines before the line /* That's all, stop editing! */:
// Personal access token (required for private repos)
define( 'FORGEJO_ACCESS_TOKEN', 'your_token_here' );
// Base URL of the Forgejo instance (optional since v2.0 — derived from the plugin header)
define( 'FORGEJO_BASE_URL', 'https://git.example.com' );
How to create FORGEJO_ACCESS_TOKEN
- Log in to your Forgejo instance with your account
- Go to Settings → Applications → Manage Access Tokens
- Create a new token with the following permissions:
- Repository → Read (to read raw files and download ZIPs)
- Everything else: No access
- Copy the generated token and paste it into
wp-config.php
An administrator account is not required: the account that owns the repositories you need to manage is enough.
Notes on the FORGEJO_ACCESS_TOKEN constant
Required for private repos. The token is used in two places:
- In the
Authorization: token xxxheader of Forgejo REST API calls - In the
Authorization: token xxxheader of the ZIP download request during the update
The token is added only to requests directed to known Forgejo URLs (from the FORGEJO_BASE_URL constant and/or from plugin headers). It is never sent to third-party domains.
Notes on the FORGEJO_BASE_URL constant
Deprecated. The Forgejo Plugin URI header should always contain the full repository URL (e.g. https://git.example.com/owner/repo) — the base URL is automatically derived from there. Defining FORGEJO_BASE_URL in wp-config.php is not needed and ties the plugin to a specific URL, hurting portability if the Forgejo instance changes.
Plugin auto-discovery
Since v2.0 the plugin automatically detects the plugins to manage by reading the header of the main PHP file. Two headers are supported, compatible with Git Updater:
/**
* Plugin Name: My Plugin
* Forgejo Plugin URI: https://git.example.com/owner/my-plugin
*/
or:
/**
* Plugin Name: My Plugin
* Gitea Plugin URI: https://git.example.com/owner/my-plugin
*/
If FORGEJO_BASE_URL is defined, the short form can also be used:
* Forgejo Plugin URI: owner/my-plugin
Plugins detected automatically appear in the table with source "auto" and cannot be removed from the list (just remove the header to exclude them).
Why in wp-config.php and not in the database
Saving credentials in the WordPress database (even encrypted) potentially exposes them to malicious plugins or database dumps. wp-config.php lies outside the webroot in standard setups and is never served by NGINX/Apache. This approach is compatible with Redis Object Cache because it does not rely on transients for credentials.
How update detection works
Full flow
WordPress scheduler (every 12h) or manual action
└── Filter site_transient_update_plugins
└── For each plugin with a Forgejo/Gitea Plugin URI header:
└── get_transient(eg_fu_release_{md5(plugin_file)})
├── Cache hit → use cached data
└── Cache miss → call Forgejo API (1 single call)
└── GET /api/v1/repos/{owner}/{repo}/raw/{file}.php?ref=main
└── Parse "Version:" from the remote source
├── Remote version > installed → inject into $transient->response
└── Equal or lower version → remove from $transient->response
Check frequency
The version-data cache is saved as a WordPress transient with a TTL of 12 hours (CACHE_TTL = 43200). This means:
- The automatic check happens every 12 hours, in sync with the native WordPress cycle
- Going to Dashboard > Updates and clicking "Check again" forces an immediate check
- The "Check for updates now" button on the plugin page explicitly flushes all transients
Version comparison
WordPress internally uses version_compare() (PHP native). The remote version is read directly from the Version: header of the main PHP file on the repo branch.
ZIP file handling during the update
Archive download
The plugin downloads the ZIP archive of the primary branch from the Forgejo repo:
https://git.example.com/owner/repo/archive/main.zip
The folder-name problem
Forgejo generates archives with a folder name like repo-main/ that does not match the plugin slug (e.g. eg-fediverso-box/). WordPress expects the extracted folder to have the same name as the slug.
The plugin handles this via the upgrader_source_selection filter, which renames the extracted folder to the correct slug before WordPress finishes the installation.
Installing a plugin from Forgejo
EG Forgejo Updater lets you install any public plugin hosted on Forgejo directly from WordPress, without downloading ZIPs, without FTP, without manual upload.
How to do it
- Go to Plugins → Forgejo Updater
- In the "Install plugin from Forgejo" section, paste the repository URL
- Click "Verify and continue"
- The plugin runs an automatic checklist:
- Valid URL format
- Recognized Forgejo/Gitea instance
- Reachable repository
- Main PHP file found
Plugin NameandVersionheaders presentForgejo Plugin URIheader present (required for automatic updates)
- If all checks pass, a preview with name, version and author is shown
- Click "Install plugin" to proceed
- Activate the plugin from the plugin list
Once installed, the plugin is automatically detected by the auto-discovery feature and will receive updates like the others.
Adding via header (auto-discovery)
For plugins already installed with the header in the main PHP file:
* Forgejo Plugin URI: https://git.emanuelegori.uno/emanuelegori/repotest
* Primary Branch: main
The plugin is detected automatically when the page is loaded, with no additional configuration.
Forgejo icon in the plugin list
In Plugins > Installed Plugins, every plugin managed by this updater shows an extra link in the meta info row (below the name and description), with the Forgejo logo and a direct link to the repository.
The SVG logo is embedded inline in PHP (get_forgejo_svg()), with no separate asset files and no additional HTTP requests.
WordPress hooks used
| Hook | Class | Purpose |
|---|---|---|
plugins_loaded |
bootstrap | Class initialization |
site_transient_update_plugins |
Updater | Inject update data into the native WP cycle |
plugins_api |
Updater | "Version details" popup in the dashboard |
plugin_row_meta |
Updater | Forgejo icon and repo link in the plugin list |
http_request_args |
Updater | Add Authorization token for private repo downloads |
upgrader_source_selection |
Updater | Fix the ZIP folder name from zipball |
upgrader_process_complete |
Updater | Cache flush after a completed update |
admin_menu |
Admin | Register submenu under Plugins |
admin_post_eg_fu_preview |
Admin | Verify repo URL and compatibility checklist |
admin_post_eg_fu_install |
Admin | Install plugin from Forgejo zip |
admin_post_eg_fu_delete |
Admin | Remove plugin from the list |
admin_post_eg_fu_check |
Admin | Force an immediate update check |
admin_notices |
Admin | Feedback notices |
Redis Object Cache compatibility
Transients are saved through native WordPress set_transient() / get_transient() / delete_transient() functions. With Redis Object Cache active they are automatically stored in Redis without extra configuration. No direct SQL queries or database bypass accesses are used, eliminating the incompatibility issue that affected Git Updater.
Publishing a new version
For every new version of a managed plugin:
- Update
Version:in the header of the plugin's main PHP file - Commit and push to the primary branch
Within 12 hours (or immediately by clicking "Check for updates now") WordPress will show the available-update notice. There is no need to create releases or tags.
Updating EG Forgejo Updater itself
This plugin manages itself thanks to the Forgejo Plugin URI header in its own main file. It updates exactly like the other plugins.
License
GPL-2.0-or-later