[[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 {{{ #!cpp #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 la función {{{ 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. Si el usuario sabe o sospecha que puede haber algún cambio remoto que le interesaría tener puede forzar manualmente la actualización de dos formas distintas según sea el caso: * '''Update''': comprobar si hay cambios en las copias remotas de las versiones ya instaladas, es decir, si hay nuevos parches de algún paquete con el mismo numero de versión ya disponible localmente. La acción propuesta si se desea actualizarse es [[BR]] {{{ #!cpp Real TolPackage::Client::RemoteUpdateAll(True); }}} * '''Upgrade''': comprobar si hay versiones nuevas de algún paquete ya disponible, es decir, con el mismo nombre pero con número de versión más avanzado. La acción propuesta si se desea usar la versión avanzada es [[BR]] {{{ #!cpp Real TolPackage::Client::RemoteUpgradeAll(True); }}} Si por algún motivo (un fallo de conexión, un problema de permisos, conflictos entre archivos compartidos, ...) un paquete quedara corrupto, es posible eliminarlo del almacén local mediante la orden [[BR]] {{{ Real TolPackage::Client::LocalClean(package); }}} Si no se especifica número de versión se borrarán todas las versiones locales de dicho paquete. Si el problema afecta a más de un paquete o tenemos un almacén demasiado antiguo que queremos actualizar, se puede limpiar por completo ejecutando la siguiente sentencia {{{ #!cpp Real TolPackage::Client::LocalCleanAll("") }}} 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; }}} Si tras la consulta se observa que hay algún paquete con parches o nuevas versiones remotas que se desean obtener entonces se puede actualizar pasando {{{Real updateSyncInfo=False}}} para que no vuelva a llamar a {{{RemoteUpdatePackSyncInfo}}} {{{ #!cpp Real TolPackage::Client::RemoteUpdateAll(False); }}} si es un parche o a {{{ #!cpp Real TolPackage::Client::RemoteUpgradeAll(False); }}} si es una nueva versión. 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 de un 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 descargarlos 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 dispositivo de almacenamiento externo. Como un paquete puede depender de otros y cada uno puede necesitar una versión de TOL mayor o menor, no es trivial saber qué paquetes van a necesitarse para poder utilizar los que se requieren explícitamente y mucho menos qué versiones nos servirán para el TOL de la máquina en la que se quieren instalar. Además, algunos paquetes cuando se utilizan por vez primera se configuran para la máquina en la que se han instalado por lo que no son portables a otra máquina de forma inmediata. Para facilitar esta tarea se utilizará la función {{{TolPackage::Client::DownloadCompatibleDeepDependencies }}} desde una máquina que sí tenga conexión a internet. {{{ #!cpp Text _.autodoc.member.DownloadCompatibleDeepDependencies = "Downloads to a local directory all deep dependencies of a list of packages " "for the last version that is compatible with a tol version.\n" "Arguments : \n" " Text localRoot : Local directory where packages will be downloaded\n" " Real cleanLocalRoot : If true, local directory will be cleaned before to download\n" " Text tol_version : Version of TOL comaptible with all packages\n" " Set package.list : List of packages without explicit version\n" "Returns true if all packages are found and downloaded."; Real DownloadCompatibleDeepDependencies( Text localRoot, Real cleanLocalRoot, Text tol_version, Set package.list) }}} 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::Apply(?); Real TolConfigManager::Save(?); }}} Desde la versión 2.0.2, la forma más segura es inhabilitar la carga de la StdLib cargando sólo TolConfigManager y haciendo las modificaciones desde una consola mediante este comando: '' tolsh -v -i -c"Set Include(TOLSH_PATH+\"StdLib/TolConfigManager/TolConfigManager.tol\");" -c"Real TolConfigManager::Config::Upgrading::TolVersion::CheckAllowed:= False; Real TolConfigManager::Config::Upgrading::TolPackage::LocalOnly := True; Real TolConfigManager::Apply(?); Real TolConfigManager::Save(?);" '' En cualquier caso sólo es necesario hacerlo una vez por máquina. == 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 = Copy(Empty); }}} * 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 antes de nada en el NameBlock mediante los #Require sin comillas. Luego el {{{Set _.autodoc.dependencies}}} se declarará vacío y el sistema lo rellenará con los nombres de los paquetes requeridos, 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. ==== Resolución de conflictos ==== Imaginemos que tenemos un paquete A que es usado por otros dos: B y C los cuales se crearon respectivamente con las versiones A.n.1 y A.n.2, lo cual indica simplemente que cada uno funciona con su versión de A correspondiente, o mejor dicho, que ha pasado cierto número de tests de control porque a más no se puede llegar. Nada impide que cada uno de ellos pueda o no funcionar con versiones anteriores o posteriores. Esos números de versión quedan recogidos en la base de datos y son consultables vía WEB. Sería posible obligar al #Require a cargar las versiones específicas con las que se ha creado cada uno pero entonces los paquetes B y C serán incompatibles por definición, es decir, no se podrían usar conjuntamente en la misma sesión, cuando en realidad pueden ocurrir varias cosas: 1. Si B funciona con la A.n.2 y C funciona con la A.n.1 daría igual que cargaras una u otra con lo que lo mejor es usar la más moderna. 2. Si B funciona con la A.n.2 pero C no funciona con la A.n.1 entoces hay que usar la más moderna. 3. Si B no fuciona con la A.n.2 pero C sí funciona con la A.n.1 entonces ha aparecido un conflicto y habría que forzar el uso de la vieja anteponiendo en el código del usuario un #Require A.n.1 a los #Require B y #Require C, pero sólo como solución temporal mientras se hacen los cambios que toquen en A, en B o en ambos de forma que B funcione con la A.n.2 Para reducir las probabilidades de que aparezcan conflictos y paliar sus consecuencias cuando ocurran, la política de desarrollo debería cumpir estas normas: 1. Los paquetes nunca han de requerir versiones concretas de un paquete ni tampoco tiene sentido que intenten cargar una versión concreta sino siempre la más avanzada que haya. 2. Cada test debe acompañarse de una batería de tests automáticos que usen sólo #Require sin especificar versiones. 3. Aplicar de forma muy cuidadosa los parches, es decir, los cambios en un paquete que no suponen cambios de versión. hay que estar muy seguro de que ningún paquete dependiente pueda verse afectado, pues no habrá forma de arreglarlo después si hay conflictos. Básicamente hay que reservarlos a temas de documentación y errores muy claros y localizados. 4. Cada vez que se quiera cambiar la versión de un paquete hay que pasar los tests del mismo en local y los de todos los paquetes que dependan de él. 5. Si falla algún test de un paquete dependiente hay que depurar los paquetes afectados para resolver el conflicto antes de que llegue al usuario. 6. Si pese a todos los cuidados ocurre un fallo entonces se reporta un ticket y, de manera excepcional y sólo mientras se arregla el problema, se colocan en el código del cliente tantos #Require de versiones específicas como sean necesarias para que puedan regresar a un estado de compatibilidad. En cuanto a los usuarios en proyectos con procesos críticos de producción es evidente que no pueden andar corriendo riesgos por lo que simplemente no deben actualizar ningún paquete en las máquinas de producción mientras no hayan comprobado que las nuevas versiones funcionan correctamente en su máquinas de desarrollo. Si queremos que se puedan usar las nuevas herramientas sin frenar el desarrollo ni arriesgar la seguridad de los clientes no se me ocurre ninguna otra forma. La automatización de === 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.