diff --git a/.github/workflows/semantic-release.yml b/.github/workflows/semantic-release.yml new file mode 100644 index 0000000..6f4a4d2 --- /dev/null +++ b/.github/workflows/semantic-release.yml @@ -0,0 +1,59 @@ +name: Semantic-Release + +on: + push: + branches: [ main ] + +permissions: + contents: write + issues: write + pull-requests: write + id-token: write + +jobs: + release: + environment: SEMANTIC_RELEASE_ENV + runs-on: ubuntu-latest + + env: + GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} + HEX_API_KEY: ${{ secrets.HEX_API_KEY }} + GITHUB_API_URL: ${{ vars.GH_API_URL }} + PUBLISH_ARTIFACT: ${{ vars.PUBLISH_ARTIFACT }} + ELIXIR_VERSION: ${{ vars.ELIXIR_VERSION }} + OTP_VERSION: ${{ vars.OTP_VERSION }} + SKIP_GIT_HOOKS: 'true' + + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.x + + - name: Set up Elixir ${{ env.ELIXIR_VERSION }} and Otp ${{ env.OTP_VERSION }} + uses: erlef/setup-beam@v1 + with: + elixir-version: ${{ env.ELIXIR_VERSION }} + otp-version: ${{ env.OTP_VERSION }} + + - name: Setup semantic-release + run: npm install -g semantic-release @semantic-release/changelog @semantic-release/github -D + + - name: Call semantic-release + run: npx -p @semantic-release/changelog semantic-release + + - name: Set variables From git tag command + if: ${{ ( env.PUBLISH_ARTIFACT == 'true' ) }} + run: | + echo "$VER" + echo "RELEASE_VERSION=$(git tag | sort --version-sort | tail -n1 | tr -d 'v')" >> $GITHUB_ENV + + - name: Install Mix dependencies + if: ${{ ( env.PUBLISH_ARTIFACT == 'true' ) }} + run: mix do local.hex --force, local.rebar --force && mix do deps.clean --unused, deps.get, deps.compile && mix deps.compile + + - name: Publish to HEX + if: ${{ ( env.PUBLISH_ARTIFACT == 'true' ) }} + run: mix hex.publish --replace --yes diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000..8412468 --- /dev/null +++ b/.releaserc @@ -0,0 +1,9 @@ +{ + "repositoryUrl": "https://github.com/bancolombia/distributed-performance-analyzer.git", + "branches": ["main"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + "@semantic-release/github" + ] +} diff --git a/README.md b/README.md index 16cee96..5301532 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ [![Forks][forks-shield]][forks-url] [![Stars][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] +[![semantic-release: angular][semantic-release-url-badge]][semantic-release-url] + [![Quality Gate Status][sonarcloud-quality-gate-shield]][sonarcloud-url] [![Maintainability Rating][sonarcloud-maintainability-shield]][sonarcloud-url] @@ -165,6 +167,8 @@ vs Mean Latency. ![Example 2 - Latency](assets/dresults_example2.png) +[semantic-release-url-badge]: https://img.shields.io/badge/semantic--release-angular-e10079?logo=semantic-release +[semantic-release-url]: https://github.com/semantic-release/semantic-release [scorecards-shield]: https://github.com/bancolombia/distributed-performance-analyzer/actions/workflows/scorecards-analysis.yml/badge.svg [scorecards-url]: https://github.com/bancolombia/distributed-performance-analyzer/actions/workflows/scorecards-analysis.yml [docker-shield]: https://img.shields.io/docker/pulls/bancolombia/distributed-performance-analyzer diff --git a/SEMANTIC-RELEASE.md b/SEMANTIC-RELEASE.md new file mode 100644 index 0000000..06e7bcf --- /dev/null +++ b/SEMANTIC-RELEASE.md @@ -0,0 +1,32 @@ +# Semantic Release +## Commit message format +semantic-release utiliza los mensajes de confirmación para determinar el impacto de los cambios en el release; Siguiendo las convenciones para mensajes de confirmación, semantic-release determina automáticamente el siguiente número de versión semántica, genera un registro de cambios y publica la versión. + +De forma predeterminada, la liberación semántica utiliza +convenciones de mensajes de confirmación angular. [convenciones de mensajes de confirmación angular.](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#-commit-message-format) + +Se pueden utilizar herramientas como commitizen o commit-lint para ayudar a los contribuyentes y hacer cumplir los mensajes de confirmación válidos. + +La siguiente tabla muestra qué mensaje de confirmación le proporciona qué tipo de versión cuando se ejecuta la versión semántica (usando la configuración predeterminada): + +| Commit Message | Release type | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | +| `fix(pencil): stop graphite breaking when too much pressure applied` | ~~Patch~~ Fix Release | +| `feat(pencil): add 'graphiteWidth' option` | ~~Minor~~ Feature Release | +| `perf(pencil): remove graphiteWidth option`

`BREAKING CHANGE: The graphiteWidth option has been removed.`
`The default graphite width of 10mm is always used for performance reasons.` | ~~Major~~ Breaking Release
(Note that the `BREAKING CHANGE: ` token must be in the footer of the commit) | +**[link a la fuente.](https://github.com/semantic-release/semantic-release?tab=readme-ov-file#Commit%20message%20format)** + +dentro de la documentación de angular sobre convenciones de mensajes se tienen estos adicionales: + +- **build:** cambios que afectan el sistema de compilación o las dependencias externas (alcances de ejemplo: gulp, broccoli, npm). +- **ci:** Cambios en nuestros archivos y scripts de configuración de CI (ejemplos: CircleCi, SauceLabs). +- **docs:** La documentación solo cambia. +- **feat:** una nueva característica. +- **fix:** una corrección de errores. +- **perf:** Un cambio de código que mejora el rendimiento. +- **refactor:** un cambio de código que no corrige un error ni agrega una característica. +- **test:** agregar pruebas faltantes o corregir pruebas existentes. + +## excluir commit del análisis del plugin: + +Todas las confirmaciones que contengan [skip release] or [release skip] en su mensaje se excluirán del análisis de confirmación y no participarán en la determinación del tipo de versión. \ No newline at end of file diff --git a/lib/domain/use_cases/metrics_collector_use_case.ex b/lib/domain/use_cases/metrics_collector_use_case.ex index 67e02da..92472a3 100644 --- a/lib/domain/use_cases/metrics_collector_use_case.ex +++ b/lib/domain/use_cases/metrics_collector_use_case.ex @@ -37,7 +37,7 @@ defmodule DistributedPerformanceAnalyzer.Domain.UseCase.MetricsCollectorUseCase end def get_metrics do - GenServer.call({:global, __MODULE__}, :get_metrics) + GenServer.call({:global, __MODULE__}, :get_metrics, 30_000) end def clean_metrics do diff --git a/lib/infrastructure/driven_adapters/mnesia/mnesia.ex b/lib/infrastructure/driven_adapters/mnesia/mnesia.ex new file mode 100644 index 0000000..d4966bb --- /dev/null +++ b/lib/infrastructure/driven_adapters/mnesia/mnesia.ex @@ -0,0 +1,137 @@ +defmodule MnesiaAdapter do + @moduledoc """ + Provides a high-level interface for interacting with the Mnesia database. + + This module encapsulates common Mnesia operations such as starting the database, + creating tables, writing records, and reading data. It also includes logging + for better observability and wraps asynchronous operations in Tasks. + """ + + require Logger + alias :mnesia, as: Mnesia + + @type table_name :: atom() + @type table_attributes :: [{atom(), atom()}] + @type record :: tuple() + @type result :: :ok | {:error, term()} + + @doc """ + Starts the Mnesia database. + + This function attempts to start Mnesia and logs the result. If Mnesia + is already running, it returns :ok. + + Returns: + * `:ok` if Mnesia started successfully or was already running + * `{:error, reason}` if there was an error starting Mnesia + """ + @spec start() :: result() + def start do + case Mnesia.start() do + :ok -> + Logger.info("#{__MODULE__}: Mnesia started successfully") + :ok + + {:error, {:already_started, _node}} -> + Logger.info("#{__MODULE__}: Mnesia already started") + :ok + + {:error, reason} -> + Logger.error("#{__MODULE__}: Error starting Mnesia: #{inspect(reason)}") + {:error, reason} + end + end + + @doc """ + Creates a new Mnesia table. + + This function creates a new table in Mnesia with the given name and attributes. + It logs the result of the operation. + + Parameters: + * `table`: The name of the table to create (atom) + * `attributes`: A list of attribute definitions for the table + + Returns: + * `:ok` if the table was created successfully + * `{:error, reason}` if there was an error creating the table + """ + @spec create_table(table_name(), table_attributes()) :: result() + def create_table(table, attributes) when is_atom(table) and is_list(attributes) do + Logger.info("#{__MODULE__}: Creating Mnesia table: #{table}") + + case Mnesia.create_table(table, attributes: attributes) do + {:atomic, :ok} -> + Logger.info("#{__MODULE__}: Mnesia table #{table} created successfully") + :ok + + {:aborted, reason} -> + Logger.error("#{__MODULE__}: Error creating Mnesia table #{table}: #{inspect(reason)}") + {:error, reason} + end + end + + @doc """ + Writes a record to Mnesia asynchronously. + + This function writes the given record to Mnesia using a dirty write operation. + The operation is wrapped in a Task for asynchronous execution. + + Parameters: + * `record`: The record to write to Mnesia (tuple) + + Returns: + * A `Task` that will resolve to `:ok` if the write was successful, + or `{:error, reason}` if there was an error + """ + @spec write(record()) :: Task.t() + def write(record) do + Task.async(fn -> + Logger.info("#{__MODULE__}: Writing data to Mnesia: #{inspect(record)}") + + case Mnesia.dirty_write(record) do + :ok -> + Logger.info("#{__MODULE__}: Data written to Mnesia successfully") + :ok + + {:error, reason} -> + Logger.error("#{__MODULE__}: Error writing data to Mnesia: #{inspect(reason)}") + {:error, reason} + end + end) + end + + @doc """ + Reads a record from Mnesia asynchronously. + + This function reads a record from Mnesia using a dirty read operation. + The operation is wrapped in a Task for asynchronous execution. + + Parameters: + * `key`: The key to read from Mnesia (can be a tuple for composite keys) + + Returns: + * A `Task` that will resolve to `{:ok, [record]}` if the read was successful, + `{:ok, []}` if no record was found, or `{:error, reason}` if there was an error + """ + @spec read(record()) :: Task.t() + def read(key) do + Task.async(fn -> + Logger.info("#{__MODULE__}: Reading data from Mnesia: #{inspect(key)}") + + case Mnesia.dirty_read(key) do + [] -> + Logger.info("#{__MODULE__}: No data found in Mnesia") + {:ok, []} + + result when is_list(result) -> + Logger.info("#{__MODULE__}: Data read from Mnesia successfully: #{inspect(result)}") + {:ok, result} + + {:error, reason} -> + Logger.error("#{__MODULE__}: Error reading data from Mnesia: #{inspect(reason)}") + {:error, reason} + end + end) + end + end \ No newline at end of file diff --git a/mix.exs b/mix.exs index 519fede..268cef4 100644 --- a/mix.exs +++ b/mix.exs @@ -13,7 +13,8 @@ defmodule DistributedPerformanceAnalyzer.MixProject do ], deps: deps(), aliases: aliases(), - metrics: true + metrics: true, + package: package() ] end @@ -88,4 +89,17 @@ defmodule DistributedPerformanceAnalyzer.MixProject do {:poolboy, "~> 1.5"} ] end + + defp package() do + [ + # This option is only needed when you don't want to use the OTP application name + name: "distributed_performance_analyzer", + # The organization the package belongs to. The package will be published to the organization repository, defaults = i"hexpm" repository. + organization: "bancolombia", + files: ["assets", "config", "hooks", "lib", "rel", "test", "Dockerfile", "LICENSE", "SECURITY.md", "README.md", "coveralls.json", "mix.lock", "mix.exs", "sonar-project.properties", ".formatter.exs", ".credo.exs", ".gitignore", ".dockerignore"], + maintainers: ["Brayan Batista Zúniga", "Alejandro Jose Tortolero Machado", "Juan David Giraldo Marin"], + licenses: ["MIT License"], + links: %{"GitHub" => "https://github.com/bancolombia/distributed-performance-analyzer.git"} + ] + end end