Skip to content

11. Plugin Updater ​

AppelTools ships a built-in plugin updater that can check, download, and install updates from six providers. The commands /atp updater and /atv updater expose the same surface on both Paper and Velocity.

11.1 Public API (common/.../Utils/PluginUpdater.java) ​

java
public class PluginUpdater {
    public static CompletableFuture<PluginUpdateInfo> checkForUpdate(PluginUpdateConfig config);
    public static CompletableFuture<Void> downloadAndReplace(PluginUpdateInfo info, Path pluginDir);
}

PluginUpdateInfo (@Data):

FieldType
providerProvider
currentVersionString
latestVersionString
downloadUrlString
fileNameString
sizelong
sha256String (optional)

Provider enum: JENKINS, SPIGOT, MODRINTH, HANGAR, GITHUB, GEYSER.

11.2 Configuration ​

Configured in config.yml under plugins:. The key of an entry is the name of the plugin:

yaml
plugins:
  my-plugin:
    provider: "modrinth"
    project-id: "abc123"
  other-plugin:
    provider: "github"
    owner: "octocat"
    name: "hello-world"
  luckperms:
    provider: "jenkins"
    api-url: "https://ci.lucko.me/job/LuckPerms"
    artifact-prefix: "LuckPerms-Bukkit-"

The keys that an entry needs depend on its provider:

ProviderThe keys it needs
jenkinsapi-url, artifact-prefix
spigotmcresource-id
modrinthproject-id
hangarplatform, owner, name
githubowner, name (or repo)
geyserproject

Optional are plugin-name, artifact-prefix, version-pattern, current-version-pattern, prerelease and repo. The version that is installed is read out of the jar of the plugin, so current-version is not a setting.

11.3 Providers ​

Each provider is an inner class of PluginUpdater that implements a uniform interface:

java
interface ProviderImpl {
    String getLatestVersion(PluginUpdateConfig config);
    String getDownloadUrl(PluginUpdateConfig config, String version);
    String getFileName(PluginUpdateConfig config, String version);
}

11.3.1 Jenkins ​

  • HTTP GET {baseUrl}/job/{jobName}/lastSuccessfulBuild/api/json.
  • Parses artifacts[0].fileName and artifacts[0].relativePath.
  • Download URL: {baseUrl}/job/{jobName}/lastSuccessfulBuild/artifact/{relativePath}.

Options:

  • jenkins-base-url (e.g. https://ci.example.com)
  • jenkins-job-name
  • jenkins-username + jenkins-api-token for basic auth (optional).

11.3.2 SpigotMC ​

  • POST to https://api.spiget.org/v2/resources/{resourceId}/versions/latest with User-Agent: AppelTools/2.0.
  • Download URL: https://api.spiget.org/v2/resources/{resourceId}/download.
  • Reads file size from Content-Length.

Options:

  • spigot-resource-id.

11.3.3 Modrinth ​

  • GET https://api.modrinth.com/v2/project/{id|slug}/version?game_versions=[...]&loaders=[...].
  • Filters by minecraft-version and loader.
  • Returns the latest matching version's primary file URL.

Options:

  • modrinth-project-id or modrinth-slug.
  • minecraft-version.
  • loader ("paper", "velocity", "folia", etc.).

11.3.4 Hangar (PaperMC) ​

  • GET https://hangar.papermc.io/api/v1/projects/{slug}/versions?channel=Release&limit=1.
  • Returns the latest version's download.url.

Options:

  • hangar-project-slug.
  • hangar-channel ("Release", "Beta", "Snapshot").
  • hangar-platform ("PAPER", "VELOCITY", "WATERFALL").

11.3.5 GitHub ​

  • GET https://api.github.com/repos/{owner}/{repo}/releases/latest.
  • Iterates assets[] to find one matching asset-pattern (glob).
  • Download URL is asset.browser_download_url.

Options:

  • github-repo (owner/repo).
  • asset-pattern (glob like *-all.jar).

11.3.6 Geyser (MCBE compatibility) ​

  • Same HTTP shape as SpigotMC, but using Geyser's update endpoint.
  • Reserved for Geyser plugin update checks.

Options:

  • geyser-project-name (defaults to "Geyser").

11.4 Download and Replace ​

java
PluginUpdater.downloadAndReplace(info, pluginDir)
    .thenAccept(v -> logger.info("Updated " + info.id()));

Steps:

  1. Download to {pluginDir}/{fileName}.tmp.
  2. Validate ZIP magic 0x50 0x4B (i.e. file starts with PK\x03\x04). If not a valid JAR, abort and delete .tmp.
  3. Delete the old file {pluginDir}/{oldFileName} (the plugin name is detected by listing *.jar in pluginDir).
  4. If the provider is SpigotMC, the downloaded file is renamed to match the existing filename (Spigot always returns the same name). For all other providers, the new filename is kept.
  5. Rename .tmp → final.

The download is done with java.net.http.HttpClient (no external dependency). For Modrinth and Hangar, an Authorization header is set if a token is in config.

11.5 Commands ​

/atp updater list ​

Lists all configured plugins with their current and latest versions.

/atp updater get <plugin> ​

Shows current vs latest for a single plugin.

/atp updater update <plugin> ​

Downloads and replaces. Requires Permissions.Command.Admin.UPDATER.

The /atv versions on Velocity expose the same surface.

11.6 Reliability ​

  • HTTP timeouts: 10s connect, 30s read.
  • Failures return a PluginUpdateException with the underlying cause.

11.7 Note on Restart ​

After downloadAndReplace, the plugin is not auto-reloaded. The user is informed to restart the proxy or paper node at their convenience. (For Paper, the bootstrap detects missing listeners at startup; for Velocity, the proxy must be restarted.)

Built with VitePress.