Mamá, quiero vender mi extensión pero soy un pelagatos


En un articulo anterior vimos como poner nuestra extensión gratis en Packagist para que cualquier se la pueda instalar en Magento mediante Composer. La cosa se pone un poco mas complicada cuando nuestra extensión es paga y por lo tanto nuestro repositorio de Github es privado.

Hacerlo, digamos que «seriamente», implicaría tener por ejemplo un servidor con Satis+Gitlab (seguramente haya muchas opciones al respecto). Esto da un poco de trabajo de instalar y sobretodo de configurar, así que vamos a ver como hacerlo sin tanto trabajo y sobretodo mucho mas barato, básicamente porque somos unos pelagatos.

Para eso vamos a usar la posibilidad que nos da Composer por medio de Artifact, esto nos permite usar un zip que guardamos en un directorio de nuestro Magento y el Composer va a tomar nuestros paquetes privados desde ese archivo, sin tener la necesidad de montar todo un andamiaje para soportarlos.

Aclaremos primero que para todos los efectos de ejemplos, vamos a tomar como base el ejemplo del articulo anterior, o sea https://github.com/gonzaloebiz/article2.

Una estructura básica de una extensión Magento es algo como:

Si instalamos esta extensión por Composer, o sea:

composer require gonzalezuy/article2

Luego que termine de instalar, vamos a ver en el directorio vendor/gonzalezuy/article2 la misma estructura, como con cualquier extensión de Magento2.

Ahora, el problema es que esta no es la manera que Artifact espera que este nuestra estructura, sino mas una estructura de Symphony, es decir un Composer.json con la información de paquete y un directorio src conteniendo los fuentes.

Obviamente no queremos modificar la estructura de muestra extensión, queremos que la estructura siga siendo la standard de Magento, y si modificar unicamente el zip que vamos a pasarle a nuestro cliente para que pueda instalar fácilmente y «a la Magento» nuestra extensión paga.

No es mi intención marearlos, pero básicamente lo que tendríamos que hacer, es bajarnos el zip de la versión de nuestra extensión, en nuestro ejemplo deberíamos ir a https://github.com/gonzaloebiz/article2/releases y ahí bajarnos el zip de la ultima versión.

Una vez abajo, extraerla, crear dentro del directorio de extracción un directorio src, mover todo el contenido a ese directorio, dejando una copia del composer.json que vamos a tener que modificar, tenemos que cambiar cambiar la parte del autoload ya que la estructura es distinta.

Nuesto composer.json original https://github.com/gonzaloebiz/article2/blob/master/composer.json tiene la siguiente forma:

{
  "name": "gonzalezuy/article2",
  "autoload": {
    "psr-4": {"Gonzalezuy\\Article2\\":""},
    "files": ["registration.php"]
  },
  "description": "An example on howto use require",
  "type": "magento2-module",
  "version": "1.0.2",
  "authors": [
    {
      "name": "Gonzalo Dominguez"
    }
  ],
  "license": [
    "OSL-3.0",
    "AFL-3.0"
  ]
}

Como dije, tenemos que hacer un pequeño cambio en el autoload, podemos ver que tanto el PSR-4 como el files deberían tener en cuenta que la estructura de la extensión ahora es distinta y que todo esta dentro del directorio src. Lo que tendríamos que hacer es cambiar esas dos lineas:

    "psr-4": {"Gonzalezuy\\Article2\\":""},
    "files": ["registration.php"]

Por estas:

    "psr-4": {"Gonzalezuy\\Article2\\":"src/"},
    "files": ["src/registration.php"]

De este modo, el composer.json del directorio raíz (no así el del directorio src/ que debería quedar como originalmente estaba) terminaría quedando de esta forma:

{
  "name": "gonzalezuy/article2",
  "autoload": {
    "psr-4": {"Gonzalezuy\\Article2\\":"src/"},
    "files": ["src/registration.php"]
  },
  "description": "An example on howto use require",
  "type": "magento2-module",
  "version": "1.0.2",
  "authors": [
    {
      "name": "Gonzalo Dominguez"
    }
  ],
  "license": [
    "OSL-3.0",
    "AFL-3.0"
  ]
}

Todo esto es un poco engorroso para que en cada versión de la extensión lo tenga que hacer a mano, así que mejor usamos un script, helo aquí:

#!/usr/bin/env bash
# Script to download asset file from tag release using GitHub API v3.
# See: http://stackoverflow.com/a/35688093/55075    
CWD="$(cd -P -- "$(dirname -- "$0")" && pwd -P)"
# Check dependencies.
set -e
type curl grep sed tr >&2
xargs=$(which gxargs || which xargs)
# Validate settings.
[ -f ~/.secrets ] && source ~/.secrets
[ "$GITHUB_API_TOKEN" ] || { echo "Error: Please define GITHUB_API_TOKEN variable." >&2; exit 1; }
[ $# -ne 3 ] && { echo "Usage: $0 [owner] [repo] [tag]"; exit 1; }
[ "$TRACE" ] && set -x
read owner repo tag <<<$@
# Define variables.
GH_API="https://api.github.com"
GH_REPO="$GH_API/repos/$owner/$repo"
GH_TAGS="$GH_REPO/releases/tags/$tag"
AUTH="Authorization: token $GITHUB_API_TOKEN"
WGET_ARGS="--content-disposition --auth-no-challenge --no-cookie"
CURL_ARGS="-LJO#"
# Validate token.
curl -o /dev/null -sH "$AUTH" $GH_REPO || { echo "Error: Invalid repo, token or network issue!";  exit 1; }
# Read asset tags.
response=$(curl -sH "$AUTH" $GH_TAGS)
GH_ASSET=$(echo "$response" | jq -r '.zipball_url')
echo "Downloading asset..." >&2
curl -o $owner'_'$repo'-'$tag.zip $CURL_ARGS -H "$AUTH" "$GH_ASSET"
unzip -q $owner'_'$repo'-'$tag.zip
rm  $owner'_'$repo'-'$tag.zip
for f in *;do
    if [[ -d $f ]]; then
        mv $f $owner'_'$repo'-'$tag
    fi
done
mkdir src
cd $owner'_'$repo'-'$tag
#sed -i "/description*/a     \"version\": \"$tag\"," composer.json
mv * ../src/
cd ..
rm -rf $owner'_'$repo'-'$tag
cp src/composer.json .
sed -i "s/\"\"/\"src\/\"/g" composer.json
sed -i "s/registration\.php/src\/registration\.php/g" composer.json
zip -qr $owner'_'$repo'-'$tag.zip src composer.json
rm -rf src composer.json

También lo podes bajar de acá https://github.com/gonzaloebiz/article2/blob/master/githubpackage.sh.

Supongo que no hay que aclarar que el autor original del script no soy, sino que como corresponde deje la referencia correspondiente en el mismo. Yo simplemente arregle algunas cosas y agregue los cambios necesarios para lo que nos atañe.

Un punto importante a aclarar, si bien Composer recomienda no poner la versión en el composer.json de la extensión, esto es necesario para poder usar Artifact, ya que Composer basa su lógica en los tags y no en el version del composer.json. Pero cuando usamos este método no tenemos acceso a los tags, por lo tanto es necesario utilizar el version. Si tu extensión no tiene la version, simplemente tenes que descomentar la linea para que la genere el script.

El script accede a tu Gitbhub, baja el zip de la versión especificada, arregla la estructura y modifica el composer.json para luego volver a armar un zip con todo correcto.

Para poder acceder a tu repo de Github, deberías crear un archivo ~./secrets (al cual le deberías dar permiso de lectura unicamente a tu usuario, o sea chmod 600) que contenga una linea del estilo:

GITHUB_API_TOKEN=tuApiKeyDeGitHub

Ahora lo que falta es llamar a nuestro script. Los parámetros que debo usar son owner (en este caso gonzaloebiz), el repo (en este caso article2) y la versión (en este caso 1.0.2), así que la llamada seria:

~/githubpackage.sh gonzaloebiz article2 1.0.2

Esto me va a dejar un zip llamado gonzaloebiz_article2-1.0.2.zip (es decir owner_repo-version.zip) que contiene el zip acomodado para poder instalarlo con Composer.

Ahora lo que tenemos que hacer, es que Composer sea capaz de instalar desde nuestro zip.

Para eso, en nuestro Magento, vamos a crear un directorio vendor/gonzalezuy/packages y ahí dentro vamos a poner nuestro zip que fue generado en el script anterior.

Ahora, lo único que queda es en una consola nos paramos en el directorio raíz de nuestro Magento y ejecutamos:

composer config repositories.gonzaloebizartifact artifact $(pwd)/vendor/gonzalezuy/packages

Esto lo que hace es agregar nuestro «repo» en nuestro composer.json, y luego simplemente lo instalamos:

composer require gonzalezuy/article2

Esto te va a instalar la extensión, incluso si previamente la habías instalado desde un Packagist privado, es decir que tenes una Packagist propio (Satis+Gitlab) al cual originalmente le habías dado acceso a tu cliente, pero por alguna razón (no pago el soporte por ejemplo) se lo quitaste, pero querés darle la nueva versión pero no acceso a tu Packagist, entonces este es un buen método.

Publicado por Gonzalo Dominguez

Magento fanatic developer. Do not try and bend the spoon. That's impossible. Instead... only try to realize the truth. There is no spoon. @gonzalezuy