{"id":4713,"date":"2026-08-28T11:24:19","date_gmt":"2026-08-28T09:24:19","guid":{"rendered":"https:\/\/davidperezgar.com\/?p=4713"},"modified":"2026-08-28T11:24:45","modified_gmt":"2026-08-28T09:24:45","slug":"automatiza-el-archivo-de-distribucion-de-tus-plugins-y-temas-en-wordpres","status":"publish","type":"post","link":"https:\/\/davidperezgar.com\/es\/automatiza-el-archivo-de-distribucion-de-tus-plugins-y-temas-en-wordpres\/","title":{"rendered":"Automatiza el archivo de distribuci\u00f3n de tus plugins y temas en WordPres"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hace un tiempo cont\u00e9 en la WordCamp Madrid c\u00f3mo generar el ZIP de distribuci\u00f3n de un plugin con WP-CLI, y lo dej\u00e9 escrito en <a href=\"\/es\/crea-archivo-distribucion-plugin-wp-cli\/\">Crea el archivo de distribuci\u00f3n de tu plugin con wp-cli<\/a>. Ah\u00ed est\u00e1 la base: el comando <code>wp dist-archive<\/code>, el <code>.distignore<\/code> y por qu\u00e9 no deber\u00edas subir a producci\u00f3n un ZIP hecho a mano.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Bueno, pues el siguiente paso es no escribir el comando nunca m\u00e1s. Cuando mantienes cientos de plugins y temas, la parte tediosa no es el comando en s\u00ed, es acordarse de todo lo que va alrededor: entrar en la carpeta, quitar las dependencias de desarrollo de Composer, generar el ZIP con el nombre correcto, dejarlo en la carpeta compartida y volver a poner las dependencias para seguir trabajando. Son cinco pasos que siempre haces igual, as\u00ed que se pueden meter en una funci\u00f3n y olvidarte.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Lo que necesitas antes de empezar<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Tres cosas, y las tres se instalan en un rato.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>WP-CLI.<\/strong> Si a\u00fan no lo tienes, en Mac lo m\u00e1s c\u00f3modo es con Homebrew:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">brew install wp-cli<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>El paquete dist-archive.<\/strong> No viene de serie en WP-CLI, hay que instalarlo aparte:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">wp package install wp-cli\/dist-archive-command<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Composer.<\/strong> Solo lo vas a necesitar si tus plugins tienen dependencias, pero conviene tenerlo:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">brew install composer<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Para comprobar que todo est\u00e1 en su sitio:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">wp --info\nwp dist-archive --help\ncomposer --version<\/code><\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">\u00bfD\u00f3nde meto la funci\u00f3n? Depende de tu shell<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Aqu\u00ed es donde mucha gente se atasca, porque copia el c\u00f3digo en el archivo equivocado y luego no le funciona nada. La cosa es sencilla: depende de qu\u00e9 shell uses.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Desde macOS Catalina, el shell por defecto es <strong>zsh<\/strong>. Antes era <strong>bash<\/strong>. Si llevas a\u00f1os con el mismo Mac arrastrando configuraciones, puedes tener las dos cosas a medias. Para salir de dudas:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">echo $SHELL<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Si te devuelve <code>\/bin\/zsh<\/code>, tu archivo es <code>~\/.zshrc<\/code>. Si te devuelve <code>\/bin\/bash<\/code>, es <code>~\/.bash_profile<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Otra forma de verlo, que te dice el shell que est\u00e1 corriendo ahora mismo:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">echo $0<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Y si quieres ver qu\u00e9 archivos de configuraci\u00f3n tienes ya creados en tu carpeta de usuario:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">ls -la ~ | grep -E \".(zshrc|bashrc|bash_profile|zprofile)\"<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Un par de matices que ahorran disgustos:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li class=\"\"><code>~\/.zshrc<\/code> se carga en cada terminal interactiva de zsh. Es el sitio natural para alias y funciones.<\/li>\n\n\n\n<li class=\"\"><code>~\/.bash_profile<\/code> se carga en shells de login de bash; <code>~\/.bashrc<\/code>, en las interactivas. En Mac lo habitual es usar <code>.bash_profile<\/code> y, si tienes <code>.bashrc<\/code>, cargarlo desde ah\u00ed con <code>source ~\/.bashrc<\/code>.<\/li>\n\n\n\n<li class=\"\">Si el archivo no existe, lo creas y ya est\u00e1: <code>touch ~\/.zshrc<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Mi recomendaci\u00f3n: si est\u00e1s en un Mac moderno, olv\u00eddate de bash y trabaja con <code>~\/.zshrc<\/code>. Y si compartes configuraci\u00f3n entre m\u00e1quinas con bash y zsh, mete la funci\u00f3n en un archivo aparte tipo <code>~\/.wp-functions.sh<\/code> y c\u00e1rgalo desde los dos con <code>source ~\/.wp-functions.sh<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">La funci\u00f3n<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Esta es la que uso a diario. Va tal cual en <code>~\/.zshrc<\/code> (o en <code>~\/.bash_profile<\/code> si sigues en bash):<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\"># Distribution Plugin WordPress.\nunalias wdis 2&gt;\/dev\/null\nfunction wdis {\n        local dir=\"${1:-.}\"\n        local out_dir=\"$HOME\/plugins\"\n\n        if [ -f \"$dir\/composer.json\" ]; then\n                composer install --no-dev --working-dir=\"$dir\" &amp;&amp; \\\n                wp dist-archive \"$dir\" \"$out_dir\" --filename-format={name}-{version} &amp;&amp; \\\n                composer install --working-dir=\"$dir\"\n        else\n                wp dist-archive \"$dir\" \"$out_dir\" --filename-format={name}-{version}\n        fi\n}<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Vamos a explicarlo por partes, que hay m\u00e1s chicha de la que parece.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>unalias wdis 2&gt;\/dev\/null<\/code> borra un posible alias antiguo con ese mismo nombre antes de declarar la funci\u00f3n. Si alguna vez tuviste <code>alias wdis=...<\/code> en el archivo, el alias gana a la funci\u00f3n y te vuelves loco buscando por qu\u00e9 no coge los cambios. El <code>2&gt;\/dev\/null<\/code> es para que no te grite si el alias no existe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>local dir=\"${1:-.}\"<\/code> coge el primer argumento que le pases y, si no le pasas ninguno, usa la carpeta actual. Es decir, puedes ejecutar <code>wdis<\/code> estando dentro del plugin, o <code>wdis ~\/proyectos\/mi-plugin<\/code> desde donde sea.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>out_dir<\/code> es la carpeta donde caen los ZIP. En mi caso, una carpeta de Google Drive que se sincroniza sola, as\u00ed que en cuanto genero el archivo ya est\u00e1 disponible para el resto del equipo. C\u00e1mbiala por la tuya. Ojo con las comillas: la ruta tiene espacios y sin comillas se rompe.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">El <code>if<\/code> comprueba si hay <code>composer.json<\/code>. Si lo hay, instala las dependencias sin las de desarrollo <code>--no-dev<\/code>), genera el ZIP y vuelve a instalarlas todas para que puedas seguir trabajando con PHPUnit, PHPCS y compa\u00f1\u00eda. Los <code>&amp;&amp;<\/code> encadenan: si algo falla por el camino, no sigue. Eso evita el escenario feo de generar un ZIP a medias.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>--filename-format={name}-{version}<\/code> deja el archivo como <code>mi-plugin-1.4.2.zip<\/code>, leyendo la versi\u00f3n de la cabecera del plugin. Para temas hace lo mismo pero leyendo el <code>Version<\/code> del <code>style.css<\/code>, as\u00ed que la funci\u00f3n te vale igual sin tocar nada.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Despu\u00e9s de guardar, recarga la configuraci\u00f3n:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">source ~\/.zshrc<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Y a partir de ah\u00ed:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">cd ~\/proyectos\/mi-plugin\nwdis<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">O directamente desde cualquier sitio:<\/p>\n\n\n\n<div class=\"cbc-code-wrapper\"><div class=\"cbc-code-bar\"><button class=\"cbc-copy-button\">Copy<\/button><\/div><pre class=\"wp-block-code\"><code class=\"\">wdis ~\/proyectos\/mi-tema<\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Errores comunes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">*<code>wdis: command not found<\/code>.** No has recargado la configuraci\u00f3n. Ejecuta <code>source ~\/.zshrc<\/code> o abre una terminal nueva. Si sigue igual, comprueba que has editado el archivo correcto con <code>echo $SHELL<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">*<code>'dist-archive' is not a registered wp command<\/code>.** Falta el paquete: <code>wp package install wp-cli\/dist-archive-command<\/code>. Pasa a menudo cuando reinstalas WP-CLI, porque los paquetes no se arrastran.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>El ZIP se genera pero le faltan archivos, o le sobran.<\/strong> Revisa el <code>.distignore<\/code> del proyecto. Sin <code>.distignore<\/code>, WP-CLI usa el <code>.gitignore<\/code>, y eso a veces excluye cosas que s\u00ed necesitas en producci\u00f3n, como el <code>vendor<\/code> de Composer.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Errores raros con la ruta de destino.<\/strong> Si la carpeta tiene espacios, tiene que ir entre comillas en todos los sitios. Y si no existe, cr\u00e9ala antes: <code>mkdir -p \"$HOME\/plugins\"<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Composer se queja de la versi\u00f3n de PHP.<\/strong> El <code>composer install --no-dev<\/code> usa el PHP de tu sistema, que puede no ser el mismo que el del servidor. Si te da problemas, a\u00f1ade <code>--ignore-platform-reqs<\/code> o configura <code>platform<\/code> en el <code>composer.json<\/code> del proyecto.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>El archivo tarda en aparecer para los dem\u00e1s.<\/strong> Si guardas en Google Drive o Dropbox, la sincronizaci\u00f3n tiene su ritmo. No mandes el enlace hasta que el icono te diga que est\u00e1 subido.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Un apunte final<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Para m\u00ed es la forma m\u00e1s segura de realizar un control de versiones de nuestro desarrollo en WordPress. Teniendo los diferentes plugins con sus versiones en un ZIP f\u00e1cil de subir y reemplazar, garantiza la subida a producci\u00f3n y adem\u00e1s va limpio de todos los archivos de desarrollo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tambi\u00e9n si vendes dichos plugins, facilitar el zip con su versi\u00f3n facilita mucho al usuario para saber qu\u00e9 est\u00e1 instalando.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Si quieres con m\u00e1s detalle como creo los archivos .distignore para crear ZIP limpios, puedes verlo en la <a href=\"\/es\/crea-archivo-distribucion-plugin-wp-cli\/\">charla<\/a> que coment\u00e9. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hace un tiempo cont\u00e9 en la WordCamp Madrid c\u00f3mo generar el ZIP de distribuci\u00f3n de un plugin con WP-CLI, y lo dej\u00e9 escrito en Crea el archivo de distribuci\u00f3n de tu plugin con wp-cli. Ah\u00ed est\u00e1 la base: el comando wp dist-archive, el .distignore y por qu\u00e9 no deber\u00edas subir a producci\u00f3n un ZIP hecho [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":4920,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":5,"footnotes":""},"categories":[168],"tags":[],"class_list":["post-4713","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-desarrollo-web"],"_links":{"self":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts\/4713","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/comments?post=4713"}],"version-history":[{"count":1,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts\/4713\/revisions"}],"predecessor-version":[{"id":5987,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/posts\/4713\/revisions\/5987"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/media\/4920"}],"wp:attachment":[{"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/media?parent=4713"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/categories?post=4713"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/davidperezgar.com\/es\/wp-json\/wp\/v2\/tags?post=4713"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}