[[PageOutline]] = !TolPackage: Un sistema de desarrollo modular en red = == Introducción == El objetivo principal de este sistema es que los usuarios finales que no colaboran en el desarrollo de las librerías de uso más o menos genérico, no tengan que preocuparse de dónde se encuentra el código fuente de las mismas, ni tenga tampoco que tener instalados programas para compartir código como CSV o SVN. El usuario sólo necesita saber una URL remota o un camino local donde se encuentran los paquetes y el nombre de los que necesita. El sistema se ocupa de instalar todo lo necesario de forma automática si se desea o manualmente si se trata de procesos críticos. El otro gran objetivo es fomentar la creación de utilidades concretas que se puedan cargar atómicamente sin necesidad de compilar cantidades ingentes de código, del cual luego se usa sólo una pequeña parte la mayoría de las veces. El usuario no necesita saber qué paquetes dependen de cuáles sino que son ellos mismos quienes se autogestionan. También resulta de vital importancia permitir que cada paquete evolucione sin excesivas ataduras de compatibilidad hacia atrás para que pueda progresar pero sin dejar tirados a los usuarios que por motivos de seguridad no pueden arriesgarse a actualizarse cada poco tiempo. Por último, debe ser muy fácil de usar. Una vez cargado, el paquete es siempre global aunque se haya llamado desde un ámbito local puesto que tratándose de herramientas de uso general no tiene sentido tener tantos miramientos. Como contrapartida, un paquete no se puede descargar de la memoria una vez que ha sido cargado, pero eso tampoco es problema puesto que no ocupan un espacio significativo en comparación con los datos de los problemas que pueden resolver. === El gestor de paquetes === El gestor de paquetes TOL es el {{{NameBlock TolPakage}}} disponible en la {{{StdLib}}} Los paquetes disponibles en repositorios remotos o locales se instalan, actualizan y manejarlos mediante las utilidades de {{{TolPackage::Client}}} Las utilidades para la creación de repositorios se encuentran en {{{TolPackage::Server}}} == Uso básico de paquetes == Un paquete es un caso de NameBlock especial que se carga con la orden {{{ #Require NombreDePaquete; }}} que se lee directamente de un archivo OIS almacenado previamente en un directorio local predeterminado por el sistema. Llamaremos almacén del cliente a ese directorio común para todas las instalaciones de TOL que haya en una misma máquina, que estará ubicado en {{{ #!cpp Text TolPackage::Client::_.localRoot }}} La orden de requerimiento {{{#Require}}} es muy diferente a las órdenes de inclusión {{{Include}}} e incrustación {{{Embed}}} por estos motivos fundamentales: * No hay que dar el camino del archivo TOL sino el nombre del paquete * No hay que poner el nombre entre comillas. * El NameBlock cargado es siempre global, se llame desde donde se llame. * No es posible deshacer un requerimiento: el paquete permanecerá disponible durante toda la sesión. * Un paquete consta exclusivamente de código declarativo, para poder residir en un .oza. La primera vez que se carga en una sesión se ejecutarán las sentencias no declarativas programadas en el método especial {{{StartActions}}} Un repositorio es un sitio web al que llamaremos también almacén del servidor, donde se encuentra disponible una colección de paquetes ya preparados para que los usuarios puedan descargarlos a su almacén de cliente. Los paquetes se encuentran comprimidos en el repositorio remoto mientras que en el almacén del cliente se encuentran los directorios descomprimidos para no perder tiempo en descomprimirlos cada vez. Aunque nos referiremos siempre al paquete como una única entidad, en realidad hay varios conceptos o estados por los que pasa * El paquete conceptualmente: sus funcionalidades * El paquete cargado: un NameBlock * El código del paquete: unos archivos TOL y recursos adicionales * El paquete en archivos: un OZA y recursos adicionales * El paquete comprimido como un archivo .zip * El paquete instalado: una carpeta en la ruta correspondiente con los archivos del paquete. * La información auxiliar publicada en el repositorio sobre el origen de su instalación y sus dependencias y en general la información necesaria para la sincronización de la copia local y la copia remota. === Instalación automática === La propia orden {{{ #!cpp #Require ThePackage }}} se encargará de descargar e instalar los paquetes que no se encuentren disponibles localmente revisando la lista de directorios apuntada por {{{ #!cpp Set TolConfigManager::Config::Upgrading::TolPackage::Repositories }}} y tomando siempre la última versión disponible que sea compatible con la versión de TOL que se esté usando, a no ser que se especifique una versión concreta {{{ #!cpp #Require ThePackage.2.4 }}} Por defecto, esta lista sólo contiene el [wiki:OfficialTolArchiveNetwork repositorio oficial de bayes] pero es posible añadir otros mediante {{{ Real TolPackage::Client::AddRepository(Text url) }}} === Actualización automática y manual === El sistema se encargará periódicamente de avisar si hay contenidos por sincronizar entre los paquetes instalados y de proponer las acciones para instalar lo que proceda en su caso. * '''Update''': comprobar si hay cambios en las copias remotas de las versiones instaladas. La acción propuesta si se desea actualizarse es [[BR]] {{{ #!cpp Real TolPackage::Client::RemoteUpdateAll(True); }}} * '''Upgrade''': comprobar si hay versiones nuevas de esos mismos paquetes. La acción propuesta si se desea usar la versión avanzada es [[BR]] {{{ #!cpp Real TolPackage::Client::RemoteUpgradeAll(True); }}} En condiciones normales, lo dicho hasta aquí es todo lo que necesita un usuario básico acerca de los paquetes TOL y los repositorios. == Uso avanzado de paquetes == === Comprobación manual del estado de la sincronización === Para saber qué paquetes y qué versiones están disponibles se puede consultar una serie de tablas de paquetes después de llamar al actualizador de la información de sincronización: {{{ #!cpp Real TolPackage::Client::RemoteUpdatePackSyncInfo(True); //Listado de todos los paquetes disponibles Set TolPackage::Client:_.packSyncInfo; //Paquetes para los que la versión remota es más moderna Set TolPackage::Client:_.packForUpdate; //Paquetes para los que hay versiones remotas ulteriores Set TolPackage::Client:_.packForUpgrade; //Paquetes no instalados Set TolPackage::Client:_.packForInstall; //Paquetes ya instalados Set TolPackage::Client:_.packInstalled; }}} Estos conjuntos devuelven tablas de registros con toda la información de cada paquete que es relevante para la sincronización de las copias locales con el repositorio remoto. {{{ #!cpp Struct @PackageSynchro { Text te_url, //URL of repository Text co_name, //Generic name of package Text te_brief, //Brief description of package Text te_min_tol_version, //Required TOL version Text co_last_version_remote, //Name of last remote version Text co_last_version_local, //Name of last local version Date dh_release_date_remote, //Publication date in remote repository Date dh_release_date_local, //Local installing date Real nu_bytes //Size of compressed file }; }}} === Instalación personalizada === El usuario puede instalar una versión concreta paquete de forma manual {{{ #!cpp Real TolPackage::Client::RemoteInstallPackage("http...","ThePackage.2.4",True); }}} o bien la última compatible si no se especifica ninguna versión {{{ #!cpp Real TolPackage::Client::RemoteInstallPackage("http...","ThePackage",True); }}} Si no se especifica la URL del repositorio se buscará en todos los disponibles. {{{ #!cpp Real TolPackage::Client::RemoteInstallPackage("","ThePackage.2.4",True); Real TolPackage::Client::RemoteInstallPackage("","ThePackage",True); }}} Es posible, aunque no recomendable de forma general, instalar todos los paquetes de un repositorio {{{ #!cpp Real TolPackage::Client::RemoteInstallFullRepository("http...",True); }}} e incluso todos los paquetes de todos los repositorios {{{ #!cpp Real TolPackage::Client::RemoteInstallFullRepository("",True); }}} Antes de efectuar una acción de este tipo sería conveniente saber cuántos paquetes hay disponibles y cuánto ocupan, pues podría llevar demasiado tiempo la descarga. === Uso off-line de paquetes === Para el uso off-line de los paquetes en una máquina sin acceso remoto es necesario instalar manualmente los paquetes. Para ello lo más sencillo es instalarlos en una máquina que sí tenga acceso y luego copiar el directorio {{{Text TolPackage::Client::_.localRoot}}} a la máquina aislada mediante algún tipo de disco o unidad flash. Para evitar que el sistema pierda tiempo intentando conectarse es recomendable cambiar la configuración local de TOL {{{ #!cpp Real TolConfigManager::Config::Upgrading::TolVersion::CheckAllowed:= False; Real TolConfigManager::Config::Upgrading::TolPackage::LocalOnly := True; Real TolConfigManager::SaveConfig(TolConfigManager::Config); }}} == Creación de paquetes == Un paquete se crea como un NameBlock pero con una serie de restricciones de obligado cumplimiento y algunas recomendaciones. === Estructura === * Debe crearse en un fichero principal del mismo nombre con extensión .tol y dentro de un directorio raíz llamado igual. El fichero principal puede cargar con #Embed los archivos auxiliares que necesite con nombre libre pero ubicación paralela o en subdirectorios del raíz. * Los miembros obligatorios del NameBlock de un paquete son: [[BR]] {{{ #!cpp Text _.autodoc.name = "NombreDelPaquete"; Text _.autodoc.brief = "Descripción cortísima en una línea"; Text _.autodoc.description = "Descripción detallada"; Text _.autodoc.url = "http://.../"; Set _.autodoc.keys = [["Palabras","Clave", ...]]; Set _.autodoc.authors = [[ "fulanito@mail.tal", "menganito@mail.cual"]]; Text _.autodoc.minTolVersion = Copy(TolReleaseId); Real _.autodoc.version.high = 1; Real _.autodoc.version.low = 1; Set _.autodoc.dependencies = [["paquete1","paquete2", ...]]; }}} * Si el paquete se está desarrollando en un entorno de edición compartida tipo CVS ó SVN es muy conveniente incluir la información acerca del estado actual en el miembro opcional {{{Text _.autodoc.versionControl}}}. Para el caso de SVN quedaría así {{{ #!cpp Text _.autodoc.versionControl = AvoidErr.NonDecAct(OSSvnInfo(".")); }}} Falta hacer la función {{{OSCvsInfo}}} equivalente para el sistema CVS. Se trata de un campo opcional pero altamente recomendado. * Cuando un paquete requiera para su ejecución de recursos ajenos a TOL será necesario añadir un nuevo miembro que será un conjunto de textos con los caminos, obligatoriamente relativos e internos al paquete, de los directorios raíces de cada uno de los recursos. Los que sean dependientes de la plataforma deben tener una entrada distinta para cada una de forma que luego sea posible cargar lo necesario. Todos los recursos así definidos pasarán a formar parte del directorio del paquete [[BR]] {{{ #!cpp Set _.autodoc.nonTolResources = { [[ Text "./resource_1", Text "./resource_2", Text "./resource_3/Linux_x86_32", Text "./resource_3/Windows_x86_32", ... ]] }; }}} === Construcción del archivo comprimido === Si sólo se quiere construir el archivo .zip correspondiente a un paquete basta con llamar a {{{ #!cpp ////////////////////////////////////////////////////////////////////////////// Text TolPackage::Server::BuildPackage( Text name, //Nombre del paquete (sin versión) Text sourceRoot, //Directorio raíz del código fuente Text destination, //Directorio de destino del paquete Real removeDir) //Si es cierto se borra el directorio descomprimido "Construye el archivo comprimido correspondiente a un paquete TOL a partir " "del código situado en el directorio fuente:\n" " \"/\"\n" "\n" "El resultado se almacena en el directorio de destino con el nombre de " "la versión específica que conste en el NameBlock del paquete:" "\n" " \"/...zip\"\n" "\n"; ////////////////////////////////////////////////////////////////////////////// }}} Para hacer una instalación manual directa basta con pasarle {{{ #!cpp Text destination = TolPackage::Client::_.localRoot, Real removeDir = False }}} Si no se quiere interferir con la instalación local es mejor pasarle {{{ #!cpp Text destination = TolPackage::Server::_.localRoot, Real removeDir = True }}} === Publicación de paquetes === Para subir un paquete ya comprimido previamente con {{{TolPackage::Server::BuildPackage}}} se usará {{{ #!cpp ////////////////////////////////////////////////////////////////////////////// Real TolPackage::Server::UploadPackage(NameBlock dbConnect, Text pkg.path.zip) "Sube al repositorio remoto un paquete desde el archivo local comprimido " "creado con TolPackage::Server::BuildPackage y dado por el argumento:\n" " Text pkg.path.zip = \"/...zip\"\n"; ////////////////////////////////////////////////////////////////////////////// }}} === Construcción y publicación de paquetes en un solo paso === La manera más sencilla de subir un paquete es mediante la función {{{ #!cpp ////////////////////////////////////////////////////////////////////////////// Real BuildAndUploadPackage( Text name, //Nombre del paquete (sin versión) Text sourceRoot, //Directorio raíz del código fuente Text repository.url, //URL del repositorio NameBlock repository.db, //Conexión a la base de datos Text checkInstallMode) //Modo de chequeo de la instalación "Construye el archivo comprimido correspondiente a un paquete TOL a partir " "del código situado en el directorio fuente:\n" " \"/\"\n" "\n" "y luego lo sube al repositorio remoto.\n" "La copia temporal del archivo comprimido del paquete se queda en el " "directorio del servidor a efectos de depuración si surgen problemas " "pero se puede borrar si se desea.\n" "Por último se chequeará la instalación según lo indicado:" " Si checkInstallMode = \"local\" se usa la copia local comprimida.\n" " Si checkInstallMode = \"remote\" se instala desde el repositorio.\n" " En otro caso no se instala nada."; ////////////////////////////////////////////////////////////////////////////// }}} que internamente llama a {{{TolPackage::Server::BuildPackage}}} y {{{TolPackage::Server::UploadPackage}}} y luego chequea opcionalmente si funciona la instalación. === Control de versiones === El objetivo del control de versiones es que en el repositorio se puedan almacenar todas las versiones binarias de cada paquete que hayan sido publicadas de forma histórica, pues sólo de esta forma se puede asegurar que cada usuario pueda utilizar la versión compatible con todo su sistema, y que pueda a lo largo del tiempo, y accediendo a las novedades que le interesen fijando su propio ritmo de actualización, sin tener que estar siempre a la última, lo cual puede ser peligroso en sistemas de mantenimiento de producción en tiempo real, ni tampoco atándose a una versión congelada que no admite mejora ninguna. * El nombre del archivo en el que se almacena una versión concreta de un paquete se obtiene como la concatenación del nombre del paquete, que debe coincidir con el miembro redundante {{{_.autodoc.name}}}, seguido de un punto, el número {{{_.autodoc.version.high}}}, otro punto y el número {{{_.autodoc.version.low}}}. * El nombre del NameBlock sin embargo es el mismo siempre, a lo largo de toda la evolución del paquete, por lo que se invocará sin usar los números de versión, salvo en la orden {{{#Require}}}, si se desea cargar una versión concreta. * No es posible por tanto cargar dos versiones distintas de un mismo paquete en la misma sesión TOL. * Sí que es posible cargar diferentes versiones de un mismo NameBlock en una misma máquina en sesiones de TOL distintas, coincidentes o no en el tiempo. * No puede haber paquetes con el mismo nombre de NameBlock ni en el mismo ni en distinto repositorio, pues todos los paquetes compartirán un mismo almacén de cliente vengan del repositorio que vengan. El sistema de instalación y mantenimiento de paquetes debe avisar si existen conflictos entre distintos repositorios. * De forma opcional el paquete puede incorporar en _.autodoc.versionControl información adicional sobre el mecanismo de control de versiones utilizado en su desarrollo (SVN, CVS, ...) que indique cómo recuperar el código fuente exacto con el que fue construido el paquete. Esta información podría usarse de forma manual y sólo en caso de tener que rehacer un paquete antiguo que se hubiera perdido o que se quisiera modificar por un problema grave, y que fuera requerido en algún proceso de vital importancia. Llegados a este caso habría que plantearse si el paquete modificado debe sobreescribir el existente en el repositorio o se debe usar sólo de forma local donde se precise. Para el caso de SVN existe una forma muy sencilla de definir este campo opcional [[BR]] {{{ Text _.autodoc.versionControl = AvoidErr.NonDecAct(OSSvnInfo(".")); }}} ==== Nomenclatura ==== Aunque no hay ninguna normativa general al respecto, salvo en el caso del repositorio público oficial de TOL en el que serán de obligado cumplimiento, se aconseja tomar las siguientes medidas con respecto a la nomenclatura de los paquetes: * Los nombres de los paquetes deberían seguir el estilo CamelCase que permite una mayor claridad y una mejor organización de los mismos si se reservan partículas con significado preestablecido. * Aumentar el primer número de forma secuencial progresiva de uno en uno, empezando desde el 1, reservando el 0 para versiones de pruebas no publicadas. Sólo se debería modificar si se da un cambio, o una acumulación de cambios, que suponga alteraciones importantes en el modo de uso o las capacidades del paquete. * Aumentar el segundo número cada vez que se modifica el paquete en cualquier cosa que pueda ser incompatible con lo anterior en cualquier aspecto por pequeño que sea. Si no se modificara al publicarlo sobreescribirá el anterior y será imposible que los usuarios utilicen la versión eliminada si por cualquier motivo la nueva no les sirve. * Cuando se aumenta el primer número se debería reiniciar el segundo. * Al segundo dígito se le puede intentar dotar de cierta semántica propia del paquete o de u proyecto o departamento, obligando a que tengan más o menos cifras. Por ejemplo, podrían ser de la forma XxYy, con X,Y=1..9; x,y=0..9; significando Yy para cierto tipo de cambios menores o más frecuentes y Xx para otros de mayor calado. * Si se produce un cambio meramente ornamental, de documentación, o que resuelva un problema menor que no pueda tener ningún tipo de efecto secundario se puede sobreescribir el paquete publicado Esto puede ser recomendable cuando un paquete es muy pesado para no derrochar demasiado espacio en el repositorio. ==== Dependencias ==== * Cada versión de un paquete requiere de una versión mínima de TOL dada por el miembro _.autodoc.minTolVersion que asegura su funcionamiento, aunque podría ser que funcionara con versiones anteriores, sin que sea fácil asegurarlo a ciencia cierta. Es de vital importancia revisar este campo antes de publicar una nueva versión de un paquete si ha habido cambios en la versión de TOL que pudieran afectarle y que hubieran ocurrido desde la publicación de la última versión del paquete. En principio sólo es preciso actualizar {{{_.autodoc.minTolVersion}}} si se hace uso de alguna nueva capacidad de TOL, lo cual no siempre es trivial conocer. En caso de duda es mejor actualizar de más que de menos. * Cuando un paquete requiere de otros se debe incluir las correspondientes sentencias #Require antes de la declaración del primer miembro. * Cuando el {{{#Require}}} no menciona la versión concreta del paquete que se desea cargar entonces se utilizará la versión más moderna existente localmente que sea compatible con la versión del TOL con el que se esté trabajando. * Si se solicita una versión concreta, entonces se debe cargar previamente cada uno de los paquetes requeridos directa o indirectamente. * Téngase en cuenta que no es posible cargar versiones concretas de paquetes que tengan requerimientos de diferentes versiones de un mismo paquete. * La definición del paquete siempre debe usar #Require sin especificar una versión concreta. * Obsérvese que los paquetes requeridos se deben especificar por duplicado: primero en los #Require sin comillas y luego en Set _.autodoc.dependencies entre comillas pues esta información la necesita el gestor de repositorios. * No está permitido el requerimiento cíclico directo ni indirecto entre paquetes, es decir, si A requiere a B directa o indirectamente B no puede requerir a A ni directa ni indirectamente. El sistema gestor de repositorios caería en un ciclo infinito y no hay forma de detectarlo luego es responsabilidad de los desarrolladores del repositorio el evitarlo. === Código declarativo === * Puesto que el NameBlock de un paquete se almacena como un módulo OZA, está terminantemente prohibido que un NameBlock ejecute acciones de ningún tipo durante su creación. Es decir, las declaraciones de los miembros no pueden tener efectos secundarios como * Cambiar parámetros globales de TOL [[BR]] {{{ PutEditor, PutLanguage, PutDefaultDates, PutDumpFile, PutTableRealFormat, PutTableDateFormat, PutRealFormat, PutDateFormat }}} * Modificar variables con [[BR]] {{{ PutValue, :=, PutName, PutSerDat, PutMatDat, PutVMatDat, PutVMatBlock, PutCoef SetIndexByName, Append }}} * Llamar al sistema operativo [[BR]] {{{ FileDelete, FileRename, FileCat, MkDir, System, ShellExecute, WinSystem, ChildProcess, WinRmtSystem, WinRmtKill, WinRmtProcessAlive }}} * Llamar a funciones del interfaz [[BR]] {{{ Tcl_Eval, Tcl_EvalEx }}} * Abrir ficheros [[BR]] {{{ ShowFile,WriteFile,AppendFile MatWriteFile,MatAppendFile,VMatPrint BDTFile,BSTFile,BMTFile,StatFile,BSIFile FOpen,FGetText,FPutText,FEof,FFlush,FClose }}} * Abrir conexiones a la base de datos o a cualquier otro mecanismo de consulta. {{{ DBOpen,DBActivate,DBGetOpened,DBClose,DBExecQuery, DBSeries,DBSeriesColumn,DBSeriesTable, DBMatrix,DBTable,DBCreateSeriesTable,DBTableColumn, BDBExtract,BDBSaveAs,BDBSeries, BDBClassify,BDBSortAndSave,BDBSort, BDBCell,BDBRead,BDBTable,BDBReg,BDBLine, BDBFieldPos,BDBClose,BDBOpen }}} * Uso de {{{MakeGlobal}}} (De hecho no debería usarse nunca) * Como excepción a lo anterior sí está permitido el uso de {{{ PutStructure }}} ya que se emplea a menudo como mecanismo de documentación interna del código fuente. * Si un paquete necesita ejecutar alguna acción antes de ser utilizado deberá tener un método [[BR]] {{{ Real StartActions(Real void) { ... }; }}} [[BR]] el cual será llamado justo después de ser cargado por el #Require por vez primera. En el caso de que el paquete contenga recursos ajenos a TOL, el método {{{StartActions}}} será quien se ocupe de terminar de inicializar lo que corresponda a esos recursos. Para ello sólo necesita saber el path local que se obtiene llamando a [[BR]] {{{ Text TolPackage::Client::LocalResourcePath(Text package.version, Text resource) }}} * Excepcionalmente, cuando se sabe positivamente que una acción no va a tener efectos secundarios y es necesario efectuarla durante la propia creación del NameBlock y no durante su inicialización puede recurrirse a la función global {{{AvoidErr.NonDecAct}}}. En principio debería restringirse a temas puramente documentales como es el caso de {{{_.autodoc.versionControl}}} === Documentación y chequeo de calidad === * Cada paquete debe estar dotado de documentación autocontenida de manera que cualquier usuario pueda aprender a usarlo por sí mismo. Queda por ver si esa documentación ha de ser WIKI, HTML, PDF, ASCII, o si puede ser opcional el formato. La documentación estará ubicada en cualquier caso dentro de un directorio doc colgando directamente de la raíz del paquete. * También debe tener una batería de tests estándar en un directorio test colgando directamente de la raíz del paquete. En [source:/tolp/OfficialTolArchiveNetwork] pueden verse ejemplos de paquetes. Cada subdirectorio es un paquete. == Creación de repositorios == Un repositorio es un sistema de almacenamiento remoto de cada una de las versiones publicadas históricamente de un grupo de paquetes gestionados por una misma organización que centralice el trabajo de uno o varios grupos de desarrolladores. El sistema debe tener un protocolo de acceso vía URL que sumunistrará todo lo necesario para la instalación y mantenimiento de paquetes. * Información general del propio repositorio: * Nombre. * Descripción. * Responsables. * Información sobre los paquetes: * Información auxiliar de una versión de un paquete. * Listado de todos los paquetes existentes. * Listado de las versiones disponibles de cada paquete. * Tabla de información auxiliar de todas las versiones de cada paquete. * Nombre de la última versión de un paquete * Nombre de la última versión de un paquete compatible con cierta versión de TOL * Listado de paquetes requeridos directamente o indirectamente por un paquete dado * Descarga de paquetes: * Archivo en formato comprimido para la descarga manual. * Archivo en formato ASCII codificado en Base64 para la descarga automática. Debería haber un comité o alguien responsable de probar todos los paquetes de un repositorio antes de publicar las actualizaciones de los paquetes para asegurar la compatibilidad conjunta de todos ellos. También se debe hacer un estudio previo al desarrollo una o varias funcionalidades para ver si es mejor hacer uno o varios paquetes. Los paquetes que contienen recursos externos muy pesados, como librerías de enlace dinámico o elementos gráficos, es mejor si se dividen en dos o más paquetes, uno para el código TOL propiamente dicho y otro por cada recurso independizable. De esta forma se minimiza el consumo de recursos en el repositorio pues sólo es necesario actualizar el paquete que haya sufrido modificaciones en cada momento.