EPM plugin modules - now pull from anywhere!

March 09, 2010

Categories: erlang, epm

EPM has been refactored to support plugin modules in order to allow packages to be pulled from sources other than GitHub. There are two plugin modules packaged with EPM, github_api and bitbucket_api. However, for performance reasons only github_api is loaded by default.

» http://github.com/JacobVorreuter/epm

Installing EPM

curl "http://github.com/JacobVorreuter/epm/raw/master/epm" > epm
chmod +x epm

Updating to most recent version

If you already have EPM then you will want to update it:

jvorreuter$ epm latest
epm v0.1.1, 2010

+ updated epm (/usr/local/bin/epm) to latest version

Configuring available plugins

First, run the config command to get an idea of what your current config values are:

jvorreuter$ epm config
epm v0.1.1, 2010

install_dir     "/Users/jacobvorreuter/dev"
build_dir       "/tmp"

Now run the config set command to specify a list of plugin modules to use (noting the absence of a space between module names in the list):

jvorreuter$ epm config --set repo_plugins [github_api,bitbucket_api]
epm v0.1.1, 2010

+ updated .epm config

By default EPM uses [github_api] as the value of repo_plugins

Writing custom plugins

EPM plugins implement the api_behavior that has the following callbacks:

-module(github_api).
-behaviour(api_behavior).

-export([package_deps/3, search/1, info/2, tags/2, 
         branches/2, download_package/2, default_vsn/0]).

View the entire module here: http://github.com/JacobVorreuter/epm/blob/master/src/github_api.erl

Plugin module callback functions

package_deps/3

The package_deps/3 function returns the list of depedencies specified in an application's epm file. An application with no dependencies does not require an epm file and should return an empty list. For both the GitHub and BitBucket api modules this function is implemented by requesting the application epm file and reading the deps property.

package_deps(User, ProjectName, Vsn) -> Deps
 User = string()
 ProjectName = string()
 Vsn = string()
 Deps = [{Project, Args}]
 Project = string()
 Args = list()

search/1

The search/1 function should return a list of repository records or an error tuple.

search(ProjectName) -> Results
 ProjectName = string()
 Results = [repository()] | {error, Reason}

info/2

The info/2 function should return a single repository record or an error tuple or undefined if the repository does not exist.

info(User, ProjectName) -> Result
 User = string()
 ProjectName = string()
 Result = repository() | undefined | {error, Reason}

tags/2

The tags/2 function returns a list of tag names as strings.

tags(User, ProjectName) -> Result
 User = string()
 ProjectName = string()
 Result = [Tag]
 Tag = string()

branches/2

The branches/2 function returns a list of branch names as strings.

branches(User, ProjectName) -> Result
 User = string()
 ProjectName = string()
 Result = [Branch]
 Branch = string()

download_package/2

The download_package/2 function takes a repository record and the vsn (tag/branch/sha) to be downloaded. There is a helper function, epm_package:download_tarball/2, that does most of the work. The plugin module is only responsible for generating the correct url. The return value, LocalProjectDir, is the path to the directory where the tarball was unpacked.

download_package(Repo, Vsn) -> LocalProjectDir
 User = string()
 ProjectName = string()
 Vsn = string()
 LocalProjectDir = string()

default_vsn/0

The default_vsn/0 function returns the default branch to use if none is specified on the command line. For a git repo this would be "master" and for a mercurial repo this would be "tip".

default_vsn() -> Result
   Result = string()

An example plugin

Let's make an example plugin named foo_api.erl

jvorreuter$ cd /src/
jvorreuter$ mkdir foo_plugin
jvorreuter$ cd foo_plugin
jvorreuter$ touch foo_api.erl

Now we can hard code some return values for our api functions:

And finally we compile the module, add it to our path and set the EPM repo_plugins config value:

jvorreuter$ erlc -pa /src/epm/ebin -I /src/epm/include foo_api.erl 
jvorreuter$ echo 'code:add_patha("/src/foo_plugin").' >> ~/.erlang
jvorreuter$ epm config --set repo_plugins [foo_api]
epm v0.1.1, 2010

+ updated .epm config

When we search for a package, the foo_api plugin module gives us back our fixed response!

jvorreuter$ epm search something
epm v0.1.1, 2010

===============================
AVAILABLE
===============================
  name: something
  owner: jkvor
  followers: 
  pushed: 
  homepage: http://jkvor.com
  description: my fake foo project
  repo plugin: foo_api
  tags:
    v1.0
    v1.1
  branches:
    master
    new_features
blog comments powered by Disqus