Version 26 (modified by 14 years ago) (diff) | ,
---|
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 nombre_de_paquete;
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
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.
Uso on-line de paquetes
La propia orden #Require
se encargará de descargar e instalar los
paquetes que no se encuentren disponibles localmente revisando la lista
de directorios apuntada por
Set TolConfigManager::Config::Upgrading::TolPackage::Repositories
Por defecto, esta lista sólo contiene el repositorio oficial de bayes pero es posible añadir otros mediante
Real TolPackage::Client::AddRepository(Text url)
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
Real TolConfigManager::Config::Upgrading::TolVersion::CheckAllowed:= False; Real TolConfigManager::Config::Upgrading::TolPackage::LocalOnly := False; 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:
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 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íFalta hacer la funciónText _.autodoc.versionControl = AvoidErr.NonDecAct(OSSvnInfo("."));
OSSvnInfo
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
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
////////////////////////////////////////////////////////////////////////////// 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" " \"<sourceRoot>/<name>\"\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" " \"<destination>/<name>.<high>.<low>.zip\"\n" "\n"; //////////////////////////////////////////////////////////////////////////////
Para hacer una instalación manual directa basta con pasarle
Text destination = TolPackage::Client::_.localRoot, Real removeDir = False
Si no se quiere interferir con la instalación local es mejor pasarle
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á
////////////////////////////////////////////////////////////////////////////// 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 = \"<directory>/<name>.<high>.<low>.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
////////////////////////////////////////////////////////////////////////////// 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" " \"<sourceRoot>/<name>\"\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
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
PutEditor, PutLanguage, PutDefaultDates, PutDumpFile, PutTableRealFormat, PutTableDateFormat, PutRealFormat, PutDateFormat
- Modificar variables con
PutValue, :=, PutName, PutSerDat, PutMatDat, PutVMatDat, PutVMatBlock, PutCoef SetIndexByName, Append
- Llamar al sistema operativo
FileDelete, FileRename, FileCat, MkDir, System, ShellExecute, WinSystem, ChildProcess, WinRmtSystem, WinRmtKill, WinRmtProcessAlive
- Llamar a funciones del interfaz
Tcl_Eval, Tcl_EvalEx
- Abrir ficheros
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)
- Cambiar parámetros globales de TOL
- 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
Real StartActions(Real void) { ... };
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étodoStartActions
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
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 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.