Skip to content
This repository has been archived by the owner on Jul 26, 2022. It is now read-only.

Plugin development

pimotte edited this page Apr 17, 2015 · 23 revisions

Plugins managed by the Syncany team are hosted on the Syncany organziation space. To create a new plugin, I'd suggest you copy an existing plugin and start from there. If you want to improve an existing plugin, simply check out the plugin like this:

git clone git@github.com:syncany/syncany-plugin-<plugin>.git
cd syncany-plugin-<plugin>
gradle eclipse

This sets Eclipse and lets you import the plugin as an Eclipse project. See the building wiki page for more details. Note that gradle is not included in the plugin repositories. To obtain it, you can either copy or symlink it from core/, or ensure you have a system-wide install of gradle.

If Eclipse does not find your nested projects you might need to generate Eclipse project files for the Syncany core project.

cd core
./gradlew eclipse

Creating a new plugin

As of now, you need to create the following classes/files to create a new plugin. Note that this might change in future versions, but we try to keep it as stable as possible. <plugin-id> is a lower case identifier of your plugin, e.g. sftp, s3, ftp. <camel-plugin-id> is the same plugin identifier, but written as with a starting upper case letter, e.g. Sftp, S3 or Ftp. No capitals other than the first letter are allowed.

Java Code:
  • Package org.syncany.plugins.<plugin-id>
  • Class org.syncany.plugins.<plugin-id>.<camel-plugin-id>TransferPlugin extending org.syncany.plugins.transfer.TransferPlugin
  • Class org.syncany.plugins.<plugin-id>.<camel-plugin-id>TransferSettings extending org.syncany.plugins.transfer.TransferSettings
  • Class org.syncany.connection.plugins.<plugin-id>.<camel-plugin-id>TransferManager extending org.syncany.plugins.transfer.AbstractTransferManager

Every plugin has a TransferManager and TransferSettings. TransferManager is implementing the the communication with the intended storage backend. Some transfer plugins might need to store settings like credentials or file paths. This can be done by reflecting those settings in a TransferSettings object which is stored by Syncany in XML representation within a repository. Supported values to store are String, Integer, Boolean, File and TransferSettings again. One can either store a concrete TransferSettings object like LocalTransferSettings or use TransferSettings to provide a wildcard transfer settings object. In the later case Syncany will ask a user which transfer plugin (from all installed plugins except the current one) he wants to use while initializing your plugin.

Creating a TransferSettings object

Syncany uses the simpleframework to persist TransferSettings objects.

@Element(name = "accessToken", required = true)
@Setup(order = 1, sensitive = true, singular = true, description = "Access token", callback = DropboxAuthPluginOptionCallback.class, converter = DropboxAuthPluginOptionConverter.class)
@Encrypted
public String accessToken;

All fields marked with the org.simpleframework.xml.Element annotation will be stored on disk, i.e. this are the persisted plugin values (username, passwords, ...).

  • name: name can be used to change the field's name within the persisted XML file.
  • required: required values cannot be skipped during initialization.

The org.syncany.plugins.transfer.Setup annotation is used to change syncany's behaviour during plugin initialization. Fallback values will be used if the fully optional annotation is omitted.

  • order: Lower ordered values will be formerly asked during the query process. Please note that if an order value is assigned multiple times, ordering cannot be guaranteed.
  • sensitive: Fields marked sensitive will not be outputted when a user enters character (recommended for passwords or similar field types).
  • singular: If a user reviews values during initialization Syncany pre fills the fields with the previously entered value. This can be disabled by setting singular to true. This might be needed for some oauth based plugins if a token is only for one-time usage.
  • description: Usually, syncany uses the field's name during initialization which might be cryptic in some cases. Therefore description can overwrite this behaviour allowing to use a more meaningful instruction.
  • callback: A callback is a class implementing org.syncany.plugins.transfer.TransferPluginOptionCallback. String preQueryCallback() returns a string which is printed before before a user enters a value. String postQueryCallback(String optionValue) is called after a user has entered the setting with the entered setting being an argument. It also returns a string which is shown to a user.
  • converter: Sometimes it is needed that an entered setting value is converted. For instance, the dropbox plugin uses this method to extract the access token from the received authorisation token. Such a converter has to implement org.syncany.plugins.transfer.TransferPluginOptionConverter. Its String convert(String input) method is then called before org.syncany.plugins.transfer.TransferPluginOptionCallback#postQueryCallback.

org.syncany.plugins.transfer.Encrypted can be used to encrypt a field value when it's persisted to disk using a machine dependent key. Therefore repository configurations with @Encrypted cannot be copied between different computers.

Finally, Syncany supports to mark different methods with the org.simpleframework.xml.core.Validate annotation. Such methods are called once all settings have been entered by user. They can be used to actually test the entered settings against different, developer defined constraints. If a constraint is breached, raising a org.syncany.plugins.transfer.StorageException with a significant error message can be used to trigger a restart of the query process.

Resources:
  • Properties file at org/syncany/connection/plugins/<plugin-id>/plugin.properties containing pluginId, pluginName, pluginVersion, pluginDate, pluginAppMinVersion and pluginRelease
  • JAR Manifest (MANIFEST.MF) containing the same information: Plugin-Id, Plugin-Name, Plugin-Version, Plugin-Date, Plugin-App-Min-Version and Plugin-Release.

Building plugin JARs

./gradlew pluginJar

Updating the core/ folder

The plugins include the main project tree in the core/ folder. This folder is managed through git subtree (also: a git subtree tutorial). Here are a few useful commands to manage this folder.

Initially (for new plugins only), you need to create an empty core/ folder and add the subtree for the core:

git remote add coreorigin -f git@github.com:syncany/syncany.git    (note the -f)
git subtree add --prefix=core coreorigin/develop --squash               (note the --squash)

Once this is done, the core/ folder contains the entire main project. It won't update/pull anything automatically. This has to be done manually (whenever needed) using the following command:

git fetch coreorigin
git subtree pull --prefix=core coreorigin develop --squash

Committing changes in core/ back to the core repository

If the plugin needs to change things in the core/ (aka the main project), just commit normally to the plugin, e.g. change code inside core/...; To commit/push back to the main project, do that:

git commit ...
git subtree push --prefix=core coreorigin develop

Further git subtree resources: http://lostechies.com/johnteague/2014/04/04/using-git-subtrees-to-split-a-repository/

Integrating the plugin in the Syncany plugin API

To automatically build a plugin, upload it to the Syncany server and register it in the plugin API (sy plugin list). A few steps are necessary:

a. Autobuild with Travis

Copy .travis.yml from another plugin

b. Adjust the API key for the plugin upload

To automatically upload your plugin to the Syncany server, please contact binwiederhier to give you an API key. If you have them, do that:

  1. Update the environment variables (- secure: ... in .travis.yml):
travis encrypt "SYNCANY_API_KEY=..."
  1. Create src/main/resources/org/syncany/connection/plugins/<plugin>/plugin.properties
  2. Update build.gradle settings, esp. dependencies to include in JAR (pluginjar)

Testing plugin changes

If you alter a plugin many times and want to test your changes, you can run the following command to install your plugin:

gradle clean pluginJar; sy plugin remove <plugin>; sy plugin install build/libs/*.jar