=== AG Theme Sync for GitHub ===
Contributors: o0reyandre10o
Tags: github, theme, deploy, sync, backup
Requires at least: 5.6
Tested up to: 7.1
Stable tag: 2.2.0
Requires PHP: 7.4
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Deploy your WordPress theme from a GitHub repository in one click, with automatic backups and rollback.

== Description ==

AG Theme Sync for GitHub updates an installed theme with the contents of a GitHub repository at the press of a button, either from the admin bar or from the plugin's own panel. It is built for workflows where the theme code lives in GitHub and you need to publish changes to production quickly and safely, without FTP, without webhooks and without external tooling.

**Features**

* **One-click sync** from the admin bar (on the front end too) or from the plugin panel.
* **Live progress**: every step (connect, download, extract, validate, back up, install, verify) is reported as it happens, with a clear notification when it finishes.
* **Automatic backups** before every sync, with one-click restore and a configurable number of copies to keep.
* **Safe installation**: the new theme is staged and validated separately, and only then replaces the current one. If anything fails, the previous version is restored automatically, so your site is never left without a theme.
* **Site verification and automatic rollback**: after updating the active theme, the plugin checks the site using the same mechanism as the WordPress theme editor and, if it detects a PHP fatal error, reverts to the previous version.
* **Change detection**: tells you whether there are new commits on GitHub that have not been deployed yet, and lists them.
* **Public and private repositories**: public ones need no token; private ones work with both classic and fine-grained tokens. The token can also be defined in `wp-config.php`.
* **Encrypted token** in the database, never displayed in full on screen.
* **Configurable branch and subdirectory**, with automatic branch detection (`main`, `master`, or the repository default branch).
* **"Verify connection" button** to check the token, repository and branch before syncing.
* **Understandable error messages** that explain the cause and how to fix it (token, permissions, SSO, API rate limit, network, SSL, file permissions, disk space, and more).
* **History** of the last 30 operations with technical details.
* **System diagnostics**: filesystem, permissions, ZipArchive, OpenSSL, outbound connections and execution time.

Developed by [Andre Gutierrez](https://toctoc.ky/team/andre-gutierrez/).

== External services ==

This plugin relies on GitHub, a third-party service, to read the repository you
configure and to download the theme source code you ask it to deploy. The plugin
cannot work without it: GitHub is where your theme code lives.

**What it connects to**

* `api.github.com` — to resolve the default branch, read the latest commit, list
  branches, compare commits, validate your credentials and request the
  repository archive (ZIP) of the branch you configured.
* `codeload.github.com` — GitHub redirects the archive request there, so the ZIP
  itself is downloaded from that host.

**What is sent**

* The repository owner and name, and the branch and subdirectory you configured.
* For private repositories, your GitHub personal access token, sent in the
  `Authorization` HTTP header. Public repositories are accessed without a token.
* A `User-Agent` header identifying the plugin and its version.

No other data is transmitted. The plugin sends nothing about your site, your
users or your content, and the plugin author receives no data of any kind: all
requests go from your server directly to GitHub.

**When it happens**

Only when an administrator asks for it: when you press "Sync", "Check for
updates" or "Verify connection", and once when you open the plugin's Dashboard
tab, which refreshes the pending-commits panel.

There are no scheduled or background requests. The plugin registers no cron
events, and the "update available" marker in the admin bar is read from the
database, not from GitHub. Nothing is requested on front-end page loads.

GitHub is operated by GitHub, Inc. Please review their terms and privacy policy:

* Terms of Service: https://docs.github.com/site-policy/github-terms/github-terms-of-service
* Privacy Statement: https://docs.github.com/site-policy/privacy-policies/github-privacy-statement

== Installation ==

1. Upload the `ag-theme-sync-for-github` folder to `/wp-content/plugins/`, or install the ZIP file from Plugins > Add New > Upload Plugin.
2. Activate the plugin from the "Plugins" menu.
3. Go to Settings > AG Theme Sync for GitHub > "Settings" tab.
4. Enter the repository URL and select the theme that will be updated. If the repository is private, add a GitHub token.
5. Press "Verify connection" and then "Save settings".
6. Use the "Sync Theme" button in the admin bar, or "Sync theme now" in the "Dashboard" tab.

Server requirements: WordPress must be able to write directly to `wp-content/themes` (the "direct" filesystem method), and the server must be able to reach `api.github.com` and `codeload.github.com`.

== Configuration ==

= GitHub token =

A token is only required for private repositories.

**Classic token**

1. Go to https://github.com/settings/tokens and press "Generate new token (classic)".
2. Give it a descriptive name and an expiry date.
3. Check the `repo` scope (required to read private repositories).
4. Generate the token, copy it and paste it into the plugin's "GitHub token" field.

**Fine-grained token (recommended)**

1. Go to https://github.com/settings/personal-access-tokens and press "Generate new token".
2. Under "Resource owner", pick the user or organization that owns the repository.
3. Under "Repository access", select only the theme repository.
4. Under "Repository permissions", grant "Contents: Read-only" ("Metadata: Read-only" is added automatically).
5. Generate the token and paste it into the plugin.

If the repository belongs to an organization that uses SSO (SAML), authorize the token for that organization from the GitHub tokens page ("Configure SSO").

The token is encrypted in the database when saved. To keep the current token, leave the field empty; to remove it, tick the corresponding checkbox.

**Token in wp-config.php**

If you would rather not store the token in the database, define it in `wp-config.php`. It takes precedence over the one saved in the settings:

`define( 'TSFG_GITHUB_TOKEN', 'ghp_your_token' );`

If you are coming from "GitHub Theme Sync" and already had `GTS_GITHUB_TOKEN` in your `wp-config.php`, it still works: there is no need to change it.

= Repository URL =

Several formats are accepted, and all are stored as `https://github.com/user/repository`:

* `https://github.com/user/my-theme`
* `https://github.com/user/my-theme.git`
* `git@github.com:user/my-theme.git`
* `user/my-theme`

= Branch =

Leave it empty for automatic detection: `main` is used first; if it does not exist, `master`; and if that does not exist either, the repository's default branch. If you enter a specific branch (for example `production` or `release/2.0`), only that one is used.

= Theme =

Select the installed theme that will be replaced by the repository contents. The entire theme folder is replaced, so any file that is not in GitHub will disappear (it is kept in the backup).

= Repository subdirectory =

Leave it empty if the theme's `style.css` is at the repository root. If the theme lives inside a folder (for example `wp-content/themes/my-theme`), enter that relative path. If you get it wrong, the error message suggests the repository folders that contain a valid theme.

= Backups =

Set how many copies to keep per theme (0 to 20; 3 by default). With 0, no copy is kept after a successful sync, although one is always created during the process so a rollback is possible.

= Site verification =

Enabled by default. After syncing the active theme (or its parent theme), the plugin loads the home page using the WordPress theme editor's check mechanism. If it detects a PHP fatal error, it restores the previous version automatically and shows the error with the file and line. If the check cannot be performed (for example, because the server cannot connect to itself), the sync continues and a notice is shown.

== Frequently Asked Questions ==

= Does it work with private repositories? =

Yes. You need a classic token with the `repo` scope, or a fine-grained token with "Contents: Read-only" on the repository. Public repositories work without a token.

= Where are backups stored? =

In `wp-content/themes/.gts-backups/{theme}/{date}-{commit}`. WordPress ignores folders starting with a dot, so backups do not show up as themes. The plugin writes an `.htaccess` that blocks web access on Apache. On Nginx, add a rule such as `location ~ /\.gts- { deny all; }`. Nothing is ever stored in the public uploads folder.

The folder keeps the `gts` prefix from when the plugin was named "GitHub Theme Sync", so that existing backups are not orphaned when upgrading.

= How does the rollback work? =

A sync follows these steps: download the ZIP to a temporary file, extract it into a temporary folder inside the themes directory, validate that it contains a theme (`style.css` with "Theme Name"), move the current theme into the backups folder, and put the new version in its place. Moving folders within the same disk is instantaneous, so the site is never left half-updated.

* If the installation fails, the backup is put back immediately.
* If site verification detects a fatal error, the new version is pulled and the previous one is restored.
* You can restore a backup at any time from the "Backups" tab; the current version is also saved as a backup before restoring.

= Can I restore manually if everything fails? =

Yes. Rename (or move) the backup folder from `wp-content/themes/.gts-backups/{theme}/{backup}` to `wp-content/themes/{theme}`. If an automatic restore ever fails, the error message states the exact paths.

= What happens if I close the page during a sync? =

The process continues on the server. Check the result in the "History" tab. While a sync is running, another one cannot be started; a lock older than 15 minutes is treated as abandoned and released automatically.

= Can I sync several themes? =

One theme is synced at a time. Change the theme and the repository in the settings if you need to update another one.

= What do the error messages mean? =

| Code | Cause | Solution |
|---|---|---|
| tsfg_not_configured | The repository URL or the theme is missing. | Complete the "Settings" tab. |
| tsfg_token_invalid | GitHub rejects the token (HTTP 401): expired, revoked or copied incorrectly. | Generate a new token and save it. |
| tsfg_token_decrypt | The salts in wp-config.php changed and the encrypted token can no longer be read. | Enter the token again and save. |
| tsfg_repo_not_found | The URL is wrong, or the token has no access (GitHub answers 404 for private repositories without permission). | Check the URL and the token permissions. |
| tsfg_forbidden | The token does not have enough permissions. | Use a classic token with `repo`, or a fine-grained token with "Contents: Read-only" on that repository. |
| tsfg_sso_required | The organization uses SSO and the token is not authorized. | Authorize the token for the organization ("Configure SSO"). |
| tsfg_rate_limited | The GitHub API rate limit has been exhausted. | Wait until the time shown, or use a token (the limit is much higher). |
| tsfg_branch_not_found | The configured branch does not exist. | Pick one of the branches listed in the message, or leave the field empty. |
| tsfg_repo_empty | The repository has no commits. | Push the theme to GitHub. |
| tsfg_github_unavailable | GitHub is having problems (5xx error). | Check https://www.githubstatus.com and try again later. |
| tsfg_http_timeout / tsfg_http_connect | The server could not reach GitHub in time. | Check the host firewall and try again. |
| tsfg_http_dns | The server cannot resolve api.github.com. | Contact your host (DNS). |
| tsfg_http_ssl | SSL certificate error on the server. | Ask your host to update the CA certificates / OpenSSL. |
| tsfg_http_blocked | WP_HTTP_BLOCK_EXTERNAL is blocking outbound connections. | Add `api.github.com,codeload.github.com` to WP_ACCESSIBLE_HOSTS. |
| tsfg_invalid_zip | The download is not a valid ZIP. | Try again; if it persists, check proxies or firewalls. |
| tsfg_fs_unavailable | WordPress cannot write files directly. | Add `define( 'FS_METHOD', 'direct' );` to wp-config.php and check file ownership. |
| tsfg_not_writable | No write permission on the themes folder. | Adjust permissions and ownership of `wp-content/themes`. |
| tsfg_disk_full / tsfg_unzip_failed | Out of disk space, or the ZIP could not be extracted. | Free up space or reduce the number of backups. |
| tsfg_subdir_not_found | The configured subdirectory does not exist in the repository. | Use one of the suggested folders, or leave the field empty. |
| tsfg_invalid_theme | There is no `style.css` with "Theme Name" at the given root. | Set the subdirectory suggested in the message. |
| tsfg_backup_failed | The backup could not be created (nothing was changed). | Check permissions and disk space. |
| tsfg_install_failed | The new version could not be put in place (the previous one was restored). | Check permissions; if the message says the restore failed, follow the manual instructions. |
| tsfg_fatal_error | The new version causes a PHP fatal error (it was rolled back). | Fix the file and line reported, push the change to GitHub and sync again. |
| tsfg_locked | A sync or restore is already running. | Wait for it to finish. |
| tsfg_nonce | The session expired. | Reload the page and try again. |
| tsfg_js_timeout | The server closed the connection before finishing (502/503/504). | Check the History: the sync may have completed. |

== Changelog ==

= 2.2.0 =
* The plugin is now called "AG Theme Sync for GitHub" (previously "GitHub Theme Sync") and its folder is now `ag-theme-sync-for-github`.
* Upgrading preserves your settings, history and backup registry automatically: nothing needs to be configured again.
* Internal change: classes, constants and options now use the `TSFG_` / `tsfg_` prefix instead of `GTS_` / `gts_`.
* The `GTS_GITHUB_TOKEN` constant in `wp-config.php` is still valid; the new one is `TSFG_GITHUB_TOKEN`.
* New: "External services" section in the readme detailing what is sent to GitHub, when and why.
* The readme and the plugin description are now written in English, so they can be translated through translate.wordpress.org.
* Documented why `rename()` is used instead of `WP_Filesystem::move()` in the emergency restore path: it runs from a shutdown handler after a PHP fatal error, where WP_Filesystem is not available and a non-atomic move could leave the site without a theme folder.
* Documented why `set_time_limit()` is raised during a sync and during an emergency restore.
* Documented that `https_local_ssl_verify` is a WordPress core filter the plugin honours, not a hook of its own.
* Tested with WordPress 7.1.

= 2.0.0 =
* New: panel with Dashboard, Settings, Backups and History tabs.
* New: automatic backups before every sync, one-click restore and a configurable number of copies (0-20).
* New: safe installation using temporary folders and instantaneous moves; the theme is never deleted or left half-installed, and is restored automatically if anything fails.
* New: site verification after syncing, and automatic rollback on a PHP fatal error.
* New: real-time step-by-step progress and notifications throughout the admin and the front end.
* New: detection of new commits on GitHub, with an indicator in the admin bar.
* New: support for public repositories without a token, and for fine-grained tokens.
* New: `GTS_GITHUB_TOKEN` constant to define the token in wp-config.php.
* New: the token is stored encrypted (AES-256) and never shown in full.
* New: configurable branch with automatic detection (main, master or the default branch) and repository subdirectory.
* New: "Verify connection" button with branch suggestions and warnings about token permissions.
* New: clear error messages with cause and solution, plus optional technical details.
* New: history of the last 30 operations, and system diagnostics.
* New: theme validation (style.css and Theme Name) with valid subdirectory suggestions and a warning if the parent theme is missing.
* New: lock that prevents simultaneous syncs.
* Improved: the download is streamed straight to a private temporary file (the ZIP is never loaded into memory or stored in uploads).
* Improved: more URL formats accepted (SSH, `.git`, `user/repository`) and repository names containing dots.
* Improved: theme cache and OPcache are cleared after every change.
* Improved: JavaScript no longer depends on jQuery.
* Improved: clean uninstall (options, backups and temporary files).
* Fixed: duplicated "Settings saved" notice.
* Fixed: repositories with dots in their name were not recognized.

= 1.0.0 =
* Initial release.
* Sync from private GitHub repositories.
* Admin bar button.
* Settings page.

== Upgrade Notice ==

= 2.2.0 =
The plugin has been renamed to "AG Theme Sync for GitHub". Your settings, history and backups are carried over automatically, and a token defined in wp-config.php keeps working untouched.

= 2.0.0 =
Fully compatible update: your settings are preserved, the current token keeps working and nothing needs to be configured again. Adds backups, automatic rollback and much clearer error messages.
