#Integración continua

0 Seguidores · 10 Publicaciones

En la ingeniería de software, la Integración continua (CI) consiste en la práctica de fusionar todas las copias de trabajo de los desarrolladores con una ruta principal, la cual se comparte varias veces al día.

Artículo Alberto Fuentes · ene 9, 2025 1m read

Cuando desplegáis código desde un repositorio, la eliminación de clases (archivos) podría no reflejarse en vuestro sistema CICD. Por ejemplo si en vuestro repositorio elimináis una clase y queréis que se refleje en el código desplegado.
Aquí tenéis un sencillo comando de una sola línea para eliminar automáticamente todas las clases en un paquete específico que no hayan sido importadas. Se puede ajustar fácilmente para una variedad de tareas adicionales:

set packages = "USER.*,MyCustomPackage.*"set dir = "C:\InterSystems\src\"set sc = $SYSTEM.OBJ.LoadDir(dir,"ck", .err, 1, .loaded)
set sc = $SYSTEM.OBJ.Delete(packages _ ",'" _ $LTS($LI($LFS(loaded_",",".cls,"), 1, *-1), ",'"),, .err2)

El primer comando compila las clases y también devuelve una lista de las clases cargadas.
El segundo comando elimina todas las clases de los paquetes especificados, excepto las clases que se cargaron justo antes en el paso anterior.

0
0 71
Artículo Alberto Fuentes · oct 5, 2023 9m read

En el vasto y variado mercado de las bases de datos SQL, InterSystems IRIS destaca como una plataforma que va mucho más allá de SQL, ofreciendo una experiencia multimodelo perfecta, y siendo compatible con un amplio conjunto de paradigmas de desarrollo. Especialmente el avanzado motor objeto-relacional ha ayudado a organizaciones a utilizar el enfoque de desarrollo más adecuado para cada una de sus cargas de trabajo intensivas en datos; por ejemplo, ingerir datos a través de Objetos y consultarlos simultáneamente mediante SQL. Las Clases Persistentes corresponden a tablas SQL, sus propiedades a columnas de la tabla y se accede fácilmente a la lógica de negocio utilizando Funciones Definidas por el Usuario o Procedimientos Almacenados. En este artículo, nos centraremos un poco en la magia que se encuentra justo debajo de la superficie y discutiremos cómo puede afectar vuestras prácticas de desarrollo e implementación. Esta es un área del producto que tenemos planificado evolucionar y mejorar, así que no dudéis en compartir vuestras opiniones y experiencias en los comentarios al artículo.

Cómo guardar la Definición de Almacenamiento

Escribir una nueva lógica de negocio es fácil, y suponiendo que tenéis APIs y especificaciones bien definidas, también lo es adaptarla o extenderla, generalmente. Pero cuando no se trata solo de lógica de negocio, sino que también involucra datos persistentes, cualquier cambio que se realice desde la versión inicial deberá poder utilizar los datos que se ingirieron a través de esa versión anterior.

En InterSystems IRIS, los datos y el código coexisten en un único motor de alto rendimiento, sin las numerosas capas de abstracción que se pueden encontrar en otros frameworks de programación 3GL o 4GL. Esto significa que solo hay un mapeo muy fino y transparente para convertir las propiedades de una clase a posiciones de $list en un nodo global por fila de datos cuando se utiliza el almacenamiento predeterminado. Si se añaden o eliminan propiedades, no se quiere que los datos de una propiedad eliminada aparezcan bajo una nueva propiedad. De este mapeo de las propiedades de clase es de lo que se encarga la Definición de Almacenamiento, un bloque XML algo críptico que se puede ver al final de la definición de clase. La primera vez que se compila una clase, se genera una nueva Definición de Almacenamiento basada en las propiedades y parámetros de la clase. Cuando se hacen cambios en la definición de clase, en el momento de la recompilación esos cambios se reconcilian con la Definición de Almacenamiento existente y se modifica para que siga siendo compatible con los datos existentes. Así que mientras uno se arriesga a refactorizar sus clases, la Definición de Almacenamiento considera cuidadosamente su creatividad anterior y se asegura de que tanto los datos antiguos como los nuevos sigan siendo accesibles. A esto le llamamos evolución del esquema.

En la mayoría de las otras bases de datos SQL, el almacenamiento físico de las tablas es mucho más opaco, si es que es visible, y los cambios solo se pueden realizar mediante sentencias ALTER TABLE. Son comandos DLL (Lenguaje de Definición de Datos) estándar, pero generalmente son mucho menos expresivos de lo que se puede lograr modificando directamente una definición de clase y código de procedimiento en IRIS. 

En InterSystems, nos esforzamos para ofrecer a los desarrolladores de IRIS la capacidad de separar de manera limpia su código y sus datos, ya que esto es crucial para garantizar un empaquetado e implementación sencilla de sus aplicaciones. La Definición de Almacenamiento juega un papel único en esto, ya que captura cómo se mapea uno al otro. Por eso, merece la pena analizarla más de cerca en el contexto de las prácticas generales de desarrollo y en particular en los flujos de CI/CD (Integración Continua/Entrega Continua).

Exportando a UDL

En el siglo actual, la administración del código fuente se basa en archivos, así que veamos primero el formato principal de exportación de archivos de IRIS. El Lenguaje de Descripción Universal (UDL) es, como su nombre indica, un formato de archivos universal para cualquier código que se escriba en InterSystems IRIS. Es el formato de exportación predeterminado cuando se trabaja con el plug-in ObjectScript-VS Code y genera archivos de fácil lectura que se asemejan casi exactamente a lo que se vería en un Entorno de Desarrollo Integrado (IDE), con un archivo .cls individual para cada clase (tabla) en la aplicación. Se puede utilizar $SYSTEM.OBJ.Export() para crear archivos UDL explícitamente, o simplemente aprovechar la integración con VS Code.

Desde la época de Studio, es posible que también recordéis un formato XML que capturaba la misma información que UDL y permitía agrupar varias clases en una sola exportación. Si bien esta última parte es conveniente en algunos escenarios, es mucho menos práctica para leer y rastrear diferencias entre versiones, por lo que lo ignoraremos por ahora.

Como UDL está diseñado para capturar todo lo que IRIS puede expresar sobre una clase, incluirá todos los elementos de una definición de clase, incluida la Definición de Almacenamiento completa. Al importar una definición de clase que ya incluye una Definición de Almacenamiento, IRIS verificará si esa Definición de Almacenamiento cubre todas las propiedades e índices de la clase y, en caso afirmativo, la tomará tal cual y sobrescribirá la Definición de Almacenamiento anterior para esa clase. Esto hace que UDL sea un formato práctico para la gestión de versiones de clases y sus Definiciones de Almacenamiento, ya que mantiene esa compatibilidad hacia atrás para los datos ingresados mediante versiones anteriores de la clase, sin importar dónde se implemente. 

Si eres un desarrollador aplicado, es posible que te preguntes si estas Definiciones de Almacenamiento siguen creciendo y si esta "carga" debe llevarse indefinidamente. El propósito de las Definiciones de Almacenamiento es preservar la compatibilidad con los datos preexistentes, por lo que si sabes que no hay ninguno de ellos y quieres deshacerte de una genealogía larga, puedes "reiniciar" tu Definición de Almacenamiento eliminándola de la definición de clase y permitiendo que el compilador de clase la genere de nuevo. Por ejemplo, puedes usar esto para aprovechar prácticas recomendadas más recientes, como el uso de Conjuntos de extensión, que implementan nombres globales hash y separan cada índice en su propio global, mejorando la eficiencia a bajo nivel. Para garantizar la compatibilidad con versiones anteriores dentro de las aplicaciones de los clientes, no podemos cambiar universalmente dichos valores predeterminados en la superclase %Persistent (aunque los aplicaremos al crear una tabla desde cero utilizando el comando DDL CREATE TABLE), por lo que vale la pena realizar una revisión periódica de tus clases y tu almacenamiento. También es posible editar directamente el XML de la Definición de Almacenamiento, pero los usuarios deben tener extrema precaución, ya que esto puede hacer que los datos existentes sean inaccesibles.

Hasta ahora, todo bien. Las Definiciones de Almacenamiento ofrecen un mapeo inteligente entre tus clases y se adaptan automáticamente a medida que el esquema evoluciona. ¿Qué más hay ahí?

¿Estático vs. Estadísticas?

Como probablemente sabes, el motor SQL de InterSystems IRIS hace uso avanzado de las estadísticas de tablas para identificar el plan de consulta óptimo para cualquier sentencia que el usuario ejecute. Las estadísticas de tablas incluyen métricas sobre el tamaño de una tabla, cómo se distribuyen los valores dentro de una columna y mucho más. Esta información ayuda al optimizador SQL de IRIS a decidir qué índice es más beneficioso, en qué orden unir tablas, etcétera. Así que, intuitivamente, cuanto más actualizadas estén las estadísticas, habrá más posibilidades de obtener planes de consulta óptimos. Desafortunadamente, hasta que introdujimos el muestreo rápido de bloques en IRIS 2021.2, recopilar estadísticas precisas de las tablas era una operación computacionalmente costosa. Por lo tanto, cuando los clientes implementaban la misma aplicación en muchos entornos en los que los patrones de datos eran en gran medida los mismos, tenía sentido considerar las estadísticas de las tablas como parte del código de la aplicación e incluirlas con las definiciones de las tablas.

Por eso en IRIS actualmente las estadísticas de las tablas se encuentran integradas dentro de la Definición de Almacenamiento. Cuando se recopilan estadísticas de tablas mediante una llamada manual a TUNE TABLE o de forma implícita mediante una consulta (ver más abajo), las nuevas estadísticas se escriben en la Definición de Almacenamiento, y los planes de consulta existentes para esta tabla se invalidan para que puedan aprovechar las nuevas estadísticas en la próxima ejecución. Como son parte de la Definición de Almacenamiento, estas estadísticas formarán parte de las exportaciones de clase UDL y, por lo tanto, pueden terminar en el repositorio de código fuente. En el caso de las estadísticas cuidadosamente revisadas para una aplicación empaquetada, esto es aconsejable, ya que se querrá que estas estadísticas específicas impulsen la generación de planes de consulta para todas las implementaciones de la aplicación.

Desde la versión 2021.2, IRIS recopila automáticamente estadísticas de tabla al inicio del plan de consultas cuando consulta una tabla que no tiene ningún tipo de estadísticas y es apta para el muestreo rápido de bloques. En nuestras pruebas, los beneficios de trabajar con estadísticas actualizadas frente a no tener estadísticas claramente superaron el coste de recopilar estadísticas en tiempo real. Sin embargo, para algunos clientes, esto ha tenido el desafortunado efecto secundario de que las estadísticas recopiladas automáticamente en la instancia del desarrollador terminan en la Definición de Almacenamiento en el sistema de control del código fuente y, finalmente, en la aplicación empaquetada. Obviamente, los datos en ese entorno de desarrollo y, por tanto, las estadísticas sobre ellos, pueden no ser representativas para una implementación real del cliente y conducir a planes de consulta no óptimos.

Este escenario es fácil de evitar. Las estadísticas de tablas se pueden excluir de la exportación de la definición de clase utilizando el calificador /exportselectivity=0 al llamar a $SYSTEM.OBJ.Export(). La configuración predeterminada del sistema para esta bandera se puede establecer utilizando [$SYSTEM.OBJ.SetQualifiers("/exportselectivity=0]. Luego se puede dejar que la recopilación automática, en la implementación final, recoja estadísticas representativas, haga que la recopilación explícita de estadísticas sea parte del proceso de implementación, lo que sobrescribirá cualquier cosa que pueda haber sido empaquetada con la aplicación, o administre las estadísticas de tablas por separado mediante sus propias funciones de importación/exportación: $SYSTEM.SQL.Stats.Table.Export() y Import().  

A largo plazo, tenemos la intención de trasladar las estadísticas de tablas para que convivan con los datos en vez de ser parte del código, y diferenciar de manera más clara entre las estadísticas configuradas explícitamente por un desarrollador y las recolectadas de los datos reales. Además, estamos planeando automatizar más el proceso de actualizar periódicamente esas estadísticas, en función de cuánto cambien los datos de la tabla con el tiempo. 

Conclusiones

En este artículo, hemos explicado el papel de una Definición de Almacenamiento en el motor IRIS ObjectRelational, cómo es compatible con la evolución del esquema y lo que implica incluirla en el sistema de control de código fuente. También hemos descrito por qué las estadísticas de tablas se almacenan actualmente en esa Definición de Almacenamiento y hemos sugerido prácticas de desarrollo para asegurarnos de que las implementaciones de las aplicaciones obtienen estadísticas que sean representativas para los datos reales del cliente. Como se mencionó anteriormente, estamos planeando mejorar aún más estas funciones, por lo que esperamos vuestros comentarios sobre la funcionalidad actual y la planificada, y mejoraremos nuestro diseño según corresponda.

0
0 170
Artículo Ricardo Paiva · nov 4, 2021 5m read

En este artículo describiré los procesos para ejecutar pruebas unitarias mediante ObjectScript Package Manager (consulta https://openexchange.intersystems.com/package/ObjectScript-Package-Manager-2), incluyendo el cálculo de la Cobertura de pruebas (mediante https://openexchange.intersystems.com/package/Test-Coverage-Tool).

Pruebas unitarias en ObjectScript

Ya hay mucha documentación sobre cómo escribir pruebas unitarias en ObjectScript, por lo que no repetiré nada de eso. Puedes consultar el Tutorial de Pruebas Unitarias aquí: https://docs.intersystems.com/irislatest/csp/docbook/Doc.View.cls?KEY=TUNT_preface

La práctica recomendada es incluir las pruebas Unitarias en algún lugar/carpeta separada en la estructura de fuentes, ya sea simplemente "/pruebas" o algo más sofisticado. Dentro de InterSystems, terminamos usando /internal/testing/unit_tests/ como nuestro estándar de facto, lo que tiene sentido porque las pruebas son internas/no distribuibles y hay otros tipos de pruebas además de las unitarias, pero esto podría ser un poco complejo para proyectos sencillos de código abierto. Puedes ver esta estructura en algunos de nuestros repositorios de GitHub.

Desde el punto de vista del flujo de trabajo, esto es súper fácil en VSCode: solo hay que crear el directorio y colocar las clases allí. Con enfoques más antiguos centrados en el servidor para el control de la fuente (los utilizados en Studio), tendrás que mapear este paquete de manera apropiada, y el enfoque para eso varía según la extensión del control de la fuente.

Desde la perspectiva de los nombres de clases para las pruebas unitarias, mi preferencia personal (y la práctica recomendada de mi grupo) es:

UnitTest.<package/class being tested>[.<method/feature being tested>]

Por ejemplo, si las pruebas unitarias son para el Método Foo en la clase MyApplication.SomeClass, la clase de la prueba unitaria se llamaría UnitTest.MyApplication.SomeClass.Foo; si las pruebas fueran para la clase en su totalidad, simplemente sería UnitTest.MyApplication.SomeClass.

Pruebas unitarias en ObjectScript Package Manager

¡Hacer que ObjectScript Package Manager esté informado de tus pruebas unitarias es sencillo! Basta con añadir una línea como la siguiente a module.xml (tomada de https://github.com/timleavitt/ObjectScript-Math/blob/master/module.xml, una bifurcación del excelente paquete matemático de @Peter Steiwer de Open Exchange, el cual utilizo como un simple ejemplo inspirador):

&lt;Module>&lt;br>  ...&lt;br>  &lt;UnitTest Name="tests" Package="UnitTest.Math" Phase="test"/>&lt;br>&lt;/Module>

Lo que todo esto significa es:

  • Las pruebas unitarias están en el directorio "tests" debajo de la raíz del módulo.
  • Las pruebas unitarias están en el paquete "UnitTest.Math". Esto tiene sentido, porque las clases que se están probando están en el paquete "Math".
  • Las pruebas unitarias se ejecutan en la fase "test" en el ciclo de vida del paquete. (También hay una fase de "verificación" en la que podrían ejecutarse, pero esa es una historia para otro día).

Cómo ejecutar pruebas unitarias

Con las pruebas unitarias definidas como se explicó anteriormente, el administrador de paquetes ofrece algunas herramientas realmente útiles para ejecutarlas. Todavía puedes configurar ^UnitTestRoot, como lo harías normalmente con %UnitTest.Manager, pero probablemente encontrarás las siguientes opciones mucho más fáciles, especialmente si estás trabajando en varios proyectos en el mismo entorno.

Puedes probar todos estas opciones clonando el repositorio objectscript-math enumerado anteriormente y luego cargarlo con zpm "load /path/to/cloned/repo/", o en tu propio paquete reemplazando "objectscript-math" con los nombres de tus paquetes (y nombres de prueba).

Para recargar el módulo y luego ejecutar todas las pruebas unitarias:

zpm "objectscript-math test"

Para simplemente ejecutar las pruebas unitarias (sin recargar):

zpm "objectscript-math test -only"

Para simplemente ejecutar las pruebas unitarias (sin recargar) y proporcionar una salida detallada:

zpm "objectscript-math test -only -verbose"

Para ejecutar un conjunto de pruebas en particular (es decir, un directorio de pruebas, en este caso, todas las pruebas en UnitTest/Math/Utils) sin recargar, y proporcionar una salida detallada:

zpm "objectscript-math test -only -verbose -DUnitTest.Suite=UnitTest.Math.Utils"

Para ejecutar un caso particular de prueba (en este caso, UnitTest.Math.Utils.TestValidateRange) sin recargar y proporcionar una salida detallada:

zpm "objectscript-math test -only -verbose -DUnitTest.Case=UnitTest.Math.Utils.TestValidateRange"

O, si solo estás resolviendo los problemas de un único método de prueba:

zpm "objectscript-math test -only -verbose -DUnitTest.Case=UnitTest.Math.Utils.TestValidateRange -DUnitTest.Method=TestpValueNull"

Cálculo de la Cobertura de pruebas mediante ObjectScript Package Manager

Así que tienes algunas pruebas unitarias, pero ¿son buenas? Calcular la cobertura de pruebas no responderá completamente a esa pregunta, pero al menos ayuda. Presenté esto en la Convención anual (Global Summit) de InterSystems, allá por el año 2018 - aquí puedes ver el vídeo: https://youtu.be/nUSeGHwN5pc .

Lo primero que tendrás que hacer es instalar el paquete de cobertura de pruebas:

zpm "install testcoverage"

Ten en cuenta que esto no requiere la instalación/ejecución de ObjectScript Package Manager; puedes encontrar más información en Open Exchange: https://openexchange.intersystems.com/package/Test-Coverage-Tool

Dicho esto, puedes aprovechar al máximo la herramienta de cobertura de pruebas si también utilizas ObjectScript Package Manager.

Antes de ejecutar pruebas, debes especificar qué clases/rutinas esperas que cubran tus pruebas. Esto es importante porque, en las bases de código muy grandes (por ejemplo, HealthShare), calcular y recopilar la Cobertura de pruebas para todos los archivos del proyecto puede requerir más memoria de la que tiene tu sistema. (Específicamente, gmheap para un análisis por linea de código, si tienes curiosidad).

La lista de archivos se incluye en un archivo llamado cover.list, que está dentro de la raíz de la prueba unitaria. Diferentes subdirectorios (conjuntos) de pruebas unitarias pueden tener su propia copia de esto para anular las clases/rutinas que se rastrearán mientras se ejecuta el conjunto de pruebas.

Para ver un ejemplo sencillo con objectscript-math, consulta: https://github.com/timleavitt/ObjectScript-Math/blob/master/tests/UnitTest/coverage.list. La guía de usuario para la Herramienta de cobertura de pruebas incluye más detalles.

Para ejecutar las pruebas unitarias con el cálculo de la Cobertura de pruebas habilitado, solo hay que añadir un argumento más al comando, especificando que se debe utilizar TestCoverage.Manager en vez de %UnitTest.Manager para ejecutar las pruebas:

zpm "objectscript-math test -only -DUnitTest.ManagerClass=TestCoverage.Manager"
 
La salida (incluso en el modo resumido) incluirá una URL donde podrás ver qué líneas de tus clases/rutinas estaban cubiertas por las pruebas unitarias, así como algunas estadísticas agregadas.

Siguientes pasos

¿Qué sucede con la automatización de todo esto en CI? ¿Qué sucede con los reportes de resultados de las pruebas unitarias y las puntuaciones/diffs de cobertura? ¡También puedes hacer eso! Para ver un ejemplo sencillo usando Docker, Travis CI y codecov.io, consulta https://github.com/timleavitt/ObjectScript-Math. Estoy planeando escribir esto en un artículo futuro que analice algunos enfoques diferentes.

0
0 133
InterSystems Official Eduardo Anglada · sep 4, 2020

¡Hola desarrolladores!

Es un placer anunciar la disponibilidad del Registro de Contenedores de InterSystems. Es un nuevo canal para que los clientes tengan acceso a las versiones finales y de prueba de software, en formato apto para contenedores. Todas las imágenes Community Edition están disponibles en un repositorio público que no necesita autenticación. Las imágenes finales (IRIS, IRIS for Health, Health Connect, System Alerting and Monitoring, InterSystems Cloud Manager) y las utilidades (como pueden ser arbiter, Web Gateway y PasswordHash) requieren un token de autenticación que se genera a partir de las credenciales de la cuenta del WRC.

0
0 159
InterSystems Official Jose-Tomas Salvador · abr 16, 2020

Estoy encantado de anunciar que InterSystems se unirá a la comunidad de código abierto (open source) en el proyecto de Extensión de Visual Studio Code para InterSystems ObjectScript. A principios de este año Raj Singh publicó que emprendíamos un viaje para redefinir el futuro de nuestra estrategia en relación al IDE, y llegamos a la conclusión de que es Visual Studio Code el IDE que puede soportar ese futuro. Es rápido, estable, rico en funcionalidad, y construido sobre una arquitectura tecnológica moderna que nos da la posibilidad de ofrecerte una funcionalidad como nunca antes para tu

0
0 124
Artículo Ricardo Paiva · oct 18, 2019 9m read

¡Hola Comunidad!

En esta serie de artículos, me gustaría presentar y discutir varios métodos posibles para el desarrollo de software con las tecnologías de InterSystems y GitLab. Trataré temas como:

  • Git 101
  • El flujo de Git (proceso de desarrollo)
  • La instalación de GitLab
  • Flujo de trabajo con GitLab
  • Entrega continua
  • Instalación y configuración de GitLab
  • Integración Continua/Entrega Continua (IC/DC) de GitLab
0
0 1090
Artículo Ricardo Paiva · sep 30, 2019 7m read

Todo el mundo tiene un entorno para realizar pruebas.

Algunas personas tienen la suerte de tener un entorno totalmente separado para Producción.

-- Anónimo

.

En esta serie de artículos, me gustaría presentar y discutir varios métodos posibles para el desarrollo de software, con las tecnologías de InterSystems y GitLab. Trataré temas como:

  • Git 101
  • Git flow (development process)
  • Instalación de GitLab
  • Flujo de trabajo de GitLab
  • GitLab CI/CD (Integración Continua/Entrega Continua)
  • CI/CD (Integración Continua/Entrega Continua) con contenedores

En esta primera parte se abordará la piedra angular del desarrollo de software moderno - el sistema de control de las versiones de Git y varios flujos de Git.

0
0 684
Artículo Alberto Fuentes · mayo 9, 2019 1m read

Me gustaría compartir un trabajo de André-Claude Gendron del CIUSSS de l'Estrie - CHUS (Canadá) presentado en un InterSystems Summit.  

Consiste en un framework de generación de objetos simulados (mocks) que puede utilizarse para construir UnitTests.

Un objeto simulado (o mock) no es más que un objeto que imita el comportamiento de objetos reales de forma controlada. Estos objetos simulados se utilizan en las pruebas unitarias (UnitTest) para simular objetos o dependencias que sean necesarias para la clase que se quiera probar. Lo explica muy bien su presentación:

0
0 947