Skip to main content
Topic: Addon Development Setup (Read 3970 times) previous topic - next topic
0 Members and 1 Guest are viewing this topic.

Addon Development Setup

Everytime I want to test an addon that I'm working on, if I've removed/added new hooks, I find I have to uninstall the previous iteration of my package and then re-install the latest build I'm working on just to test the changes.

How does everyone else handle this? Does anyone use scripts to re-integrate all the hooks from a newly-updated addon? Am I just missing something obvioius to make this easier?

Re: Addon Development Setup

Reply #1

What I do is a partially hard-coded trick: I use a file that I require_once just after the reloadSettings(); in index.php, the file looks something like:
Code: [Select]
init_CertainAddon();

function init_CertainAddon()
{
$hooks = array(
array(
'integrate_some_hook',
'the_integration_function',
'SOURCEDIR/CertainAddon.integrate.php',
),
);
foreach ($hooks as $hook)
add_integration_function($hook[0], $hook[1], $hook[2], false);
}
The "trick" is the last "false" of the add_integration_function, that tells the system not to store permanently the hook, that way I just need to change that file and the hooks are updated automagically.
Then, when the development is finished, I copy the array of hooks into the installation code of the addon and that's it, ready to ship. :D

The advantage of having each addon enclosed in a function is that I can enable/disable them just by commenting or de-commenting the line that calls the function.

HTH!
Bugs creator.
Features destroyer.
Template killer.

Re: Addon Development Setup

Reply #2

Vagrant. Just create a new VM for every addon you are working on. Doesn't take any more space on your drive and you can test anything any time.

Re: Addon Development Setup

Reply #3

Thanks @emanuele, I'll give that a try.

Even with Vagrant though, @Joshua Dickerson, how do you integrate the hooks from the addon each time you update?

Re: Addon Development Setup

Reply #4

Ah, I totally misunderstood the question. Use Vagrant to handle the issue of uninstallation and having a clean install. Then, after you do the install of SMF, do a query to add the hook.

Re: Addon Development Setup

Reply #5

I've come across an issue. I'm using a Vagrant environment now with a shell provisioning script to grab the Elkarte master branch from GitHub, populate the database with test data, copy-in a Settings.php and patch index.php to require a file to add temmporary hooks per @emanuele 's suggestion.

One of my integration functions hooks into "integrate_actions" to add a new action to point to a new controller. This is the point my process falls apart, since Elk requires that the new controller be placed into the controller directory, which I cannot do with my Vagrant synced folder. I'm trying to isolate Elk from my addons, so that everything is pluggable.

Can anyone think of an easy way to get Elk's action dispatcher to work from a directory filled with the addon files in the same directory strucutre as Elk itself? Would it be wise to allow Elk to install addons and their associated files to isolated directories instead of merging files into the core? This would make it much easier to update both addons and Elk itself.

Re: Addon Development Setup

Reply #6

If we had an "addons" directory in the boarddir, then inside this, a directory for each addon, where the package .zip would be extracted to. In the root of addons/<addon subdir> would lie a directory structure identical to Elk's own. Methods such as the action dispatcher could then use addons/*/sources/controllers as a search path along with sources/controllers.

Re: Addon Development Setup

Reply #7

For anyone curious, this is my Vagrant provisioning shell script for my Elk addon development environment.

Code: [Select]
#!/bin/bash

echo "Provisioning virtual machine..."
apt-get update > /dev/null

# Git
echo "Installing Git"
apt-get install git -y > /dev/null

# Nginx
echo "Installing Nginx"
apt-get install nginx -y > /dev/null

# PHP
echo "Updating PHP repository"
apt-get install python-software-properties -y > /dev/null
add-apt-repository ppa:ondrej/php5-5.6 -y > /dev/null
apt-get update > /dev/null

echo "Installing PHP"
apt-get install php5-common php5-dev php5-cli php5-fpm -y > /dev/null

echo "Installing PHP extensions"
apt-get install curl php5-curl php5-gd php5-mcrypt php5-mysql -y > /dev/null

# MySQL
echo "Preparing MySQL"
apt-get install debconf-utils -y > /dev/null
debconf-set-selections <<< "mysql-server mysql-server/root_password password 1234"
debconf-set-selections <<< "mysql-server mysql-server/root_password_again password 1234"

echo "Installing MySQL"
apt-get install mysql-server -y > /dev/null

# Fetch Elkarte
echo "Fetching Elkarte from GitHub"
apt-get install unzip -y > /dev/null
cd /tmp
wget https://github.com/elkarte/Elkarte/archive/v1.0.4.tar.gz > /dev/null
tar -xf v1.0.4.tar.gz
mv /tmp/Elkarte-1.0.4/* /var/www/

# Setup Elkarte
echo "Setting up Elkarte"
cat /tmp/provision/database.sql | mysql -u root -p1234
cp /tmp/provision/Settings.php /var/www/

# Patch index.php for addon hooks
cd /var/www
patch index.php /tmp/provision/index_patch.diff

# Nginx Configuration
echo "Configuring Nginx"
cp /tmp/provision/nginx_vhost /etc/nginx/sites-available/nginx_vhost > /dev/null
ln -s /etc/nginx/sites-available/nginx_vhost /etc/nginx/sites-enabled/

rm -rf /etc/nginx/sites-available/default

# Restart Nginx for the config to take effect
service nginx restart > /dev/null

# Fix permissions
chown -R www-data:www-data /var/www
chmod -R 755 /var/www

echo "Finished provisioning"

Re: Addon Development Setup

Reply #8

Quote from: ant59 – If we had an "addons" directory in the boarddir, then inside this, a directory for each addon, where the package .zip would be extracted to. In the root of addons/<addon subdir> would lie a directory structure identical to Elk's own. Methods such as the action dispatcher could then use addons/*/sources/controllers as a search path along with sources/controllers.
That's more or less my idea as well. :D
In 1.1 I already added a way to "plug" addons from an addons directory (and each addon in its own sub-dir... though I forgot to add the addon directory itself to the repo now that I look at it... xD)[1], what is not there is a way to "enforce" the controller/subs structure in the addon dir, mainly because I'm not entirely sure it is (yet?) worth. It may be not so difficult if we were to rely on namespaces and autoloading, actually, doing so it should be rather easy, but that's not something I can do now (already too many branches going on in paralel, I have to finish some, so if anyone wants to give it a crank, feel free!).
And also a way to let Elk "discover" new addons dropped there without rely on install code (well, it's slightly more complex but that's the idea).
Bugs creator.
Features destroyer.
Template killer.

Re: Addon Development Setup

Reply #9

Could you point me to the relevant branch and commits for this pluggable addon loading please? I'll see what I can do to flesh it out.

Re: Addon Development Setup

Reply #10

The branch is development (don't stare at it too much, you could lose your sanity! LOL).
The commits... well several.

Let's see... okay, I guess the most useful from this point of view is the Hooks class (in sources), in particular loadIntegrations is the method that takes care of initialize the hooks of the (enabled) addon (should be easy to read).
The idea could be to make mandatory for addons to use the "base" namespace "ElkArte\Addon\AddonName". And then from here on: "ElkArte\Addon\AddonName\Controller" and "ElkArte\Addon\AddonName\Subs" (or something similar), etc.
Finally, playing with the autoloader, it should be relatively easy to automagicaly load whatever you want, just using the whole namespace.

I hope it makes some sense...

Just a little code as example.
A possible addon could look like:
Code: (/sources/addons/Test/Test.integrate.php) [Select]
<?php

namespace ElkArte\Addon\Test;

class Test_Integrate
{
public static function register()
{
// $hook, $function, $file
return array(
array(
'integrate_something',
'ElkArte\\Addon\\Test\\Controller\\Test_Controller',
)
);
}
}

Code: (/sources/addons/Test/Controller/Test.controller.php) [Select]
<?php

namespace ElkArte\Addon\Test\Controller;

class Test_Controller extends \Action_Controller
{
    public function action_index()
    {
         [...]
    }
}

I think...
Bugs creator.
Features destroyer.
Template killer.

Re: Addon Development Setup

Reply #11

So, it's mostly there as far as I can tell, right? We need the autoloader to understand where to look for classes under addon namespaces. Hooks.class.php loads all the integration hooks from $modSettings['autoload_integrate'] for addons, so I assume that we just need to populate $modSettings['autoload_integrate'] when installing an addon, and that takes care of the integration hooks, which in turn call fully qualified (or not, if the integration files are namespaced too) class names, which the autoloader understands. Am I missing anything?

Re: Addon Development Setup

Reply #12

Yep, pretty much that.
With the bonus that if you use a file named "Something.integrate.php", the system (via discoverIntegrations) is able to recognize it and add an entry to the Core Features page, then with another static method named "setting_callback", you can enable or disable the addon from there.

So, continuing the example before:
Code: (/sources/addons/Test/Test.integrate.php) [Select]
<?php

namespace ElkArte\Addon\Test;

class Test_Integrate
{
public static function setting_callback($value)
{
if ($value)
{
Hooks::get()->enableIntegration('Test_Integrate');
}
else
{
Hooks::get()->disableIntegration('Test_Integrate');
}
}

public static function register()
{
[...]
}
}

Yeah, I guess that if the autoloader learns how to deal with the addons namespace it would be enough.
I think it would also be nice to add the namespace in loadIntegrations and loadIntegrationsSettings, like:
Code: [Select]
$class = '\\ElkArte\\Addons\\' . $class . '\\' . $class;
so that we don't need to store the namespace in the database. :D
Bugs creator.
Features destroyer.
Template killer.

Re: Addon Development Setup

Reply #13

Okay, all sounds good.

Oh I did miss something, we also need to make the dispatcher object-oriented so that it's calling methods for the autoloader to deal with instead of defining files from the sources/controllers directory in the actions array.

I'm going to go ahead and fork the branch and try to get all this working. I desperately need it for the addons I'm working on.