bug_bunny 4.9.0 → 4.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d8e96ff8b1993700ead4fa98d3ce5a2484e6f13b59d63dbd270374101ef8c28
4
- data.tar.gz: 856e18a1802e069b6446d7bb0960f8efd6e72a93570379bd301097ea5230812d
3
+ metadata.gz: af9746f85e544f059873513a7ace9b478acb6664b9053bb90d1963afb1d72848
4
+ data.tar.gz: 32854fb9e2a67f8e5cb537550d957704443d95183d22d5305b48c3207edaf05b
5
5
  SHA512:
6
- metadata.gz: 92ca661889aeb364e72ec79934b1d27a227e8fe7ed71e772298bcc682d53076388481a5ffaa8d0ca51935031acaa664b859b7a5a1fc69f785c210ed9f78c2a22
7
- data.tar.gz: f2e781eb9b13628edd52cdb0c5aceadc655128bb67c63f70c00775b6c5411429920b46b7d63d1dcb6fe83c2d5eb4ae5e5cb9a4152ab7f13e83274c9341d34fa4
6
+ metadata.gz: 90169fb942803e87f47cf1af2fb2b4cb4d66112aaf4ab6632bb6a8d5031383e1fc0105a4cf2449eed847822509a71c1e4695c8cacb9907889c286cc5cfefe70b
7
+ data.tar.gz: 39262e92b89fb583769bcfaa8a17972584b0aaeda9f197ff3cf7cf4c45941ca0ee0b3aa154e8073d4004f5213bb1852aa16cc6c6f809951b2bcf3c76b85fa66d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.9.1] - 2026-04-06
4
+
5
+ ### Correcciones
6
+ - Corregir `ArgumentError` en `Controller#process` con ActiveSupport 8.1: `Rails.logger.tagged` ahora pasa el logger como argumento al bloque (`yield self`), lo que causa error de aridad en lambdas estrictos. Se reemplaza `lambda do` por `proc do` en `core_execution` para ignorar argumentos extra. Compatible con Rails 6, 7 y 8. — @Gabriel
7
+
3
8
  ## [4.9.0] - 2026-04-05
4
9
 
5
10
  ### ✨ New Features
@@ -176,7 +176,7 @@ module BugBunny
176
176
 
177
177
  safe_log(:info, 'consumer.message_received', method: http_method, path: path,
178
178
  routing_key: delivery_info.routing_key, **otel_fields)
179
- safe_log(:debug, 'consumer.message_received_body', body: body.truncate(200))
179
+ safe_log(:info, 'consumer.message_received_body', body: body&.truncate(500), body_size: body&.size || 0)
180
180
 
181
181
  # ===================================================================
182
182
  # 3. Ruteo Declarativo
@@ -256,7 +256,7 @@ module BugBunny
256
256
  rescue StandardError => e
257
257
  safe_log(:error, 'consumer.execution_error', duration_s: duration_s(start_time), **exception_metadata(e))
258
258
  safe_log(:debug, 'consumer.execution_error_backtrace', backtrace: e.backtrace.first(5).join(' | '))
259
- handle_fatal_error(properties, 500, 'Internal Server Error', e.message)
259
+ handle_fatal_error(properties, 500, 'Internal Server Error', e.message, e)
260
260
  session.channel.reject(delivery_info.delivery_tag, false)
261
261
  end
262
262
 
@@ -267,7 +267,12 @@ module BugBunny
267
267
  # @param correlation_id [String] ID para correlacionar la respuesta con la petición original.
268
268
  # @return [void]
269
269
  def reply(payload, reply_to, correlation_id)
270
- safe_log(:debug, 'consumer.rpc_reply', reply_to: reply_to, messaging_message_id: correlation_id)
270
+ safe_log(:info, 'consumer.rpc_reply',
271
+ reply_to: reply_to,
272
+ messaging_message_id: correlation_id,
273
+ response_status: payload[:status],
274
+ response_body: payload[:body]&.to_json&.truncate(500),
275
+ response_body_size: payload[:body]&.to_json&.size || 0)
271
276
  otel_headers = BugBunny::OTel.messaging_headers(
272
277
  operation: 'publish',
273
278
  destination: '',
@@ -287,14 +292,20 @@ module BugBunny
287
292
  # Maneja errores fatales asegurando que el cliente reciba una respuesta.
288
293
  # Evita que el cliente RPC se quede esperando hasta el timeout.
289
294
  #
295
+ # @param properties [Bunny::MessageProperties] Headers y propiedades AMQP.
296
+ # @param status [Integer] Código de estado HTTP.
297
+ # @param error_title [String] Título del error.
298
+ # @param detail [String] Detalle del error.
299
+ # @param exception [StandardError, nil] Excepción original (para status 500).
290
300
  # @api private
291
- def handle_fatal_error(properties, status, error_title, detail)
301
+ def handle_fatal_error(properties, status, error_title, detail, exception = nil)
292
302
  return unless properties.reply_to
293
303
 
294
- error_payload = {
295
- status: status,
296
- body: { error: error_title, detail: detail }
297
- }
304
+ body = { error: error_title, detail: detail }
305
+
306
+ body[:bug_bunny_exception] = BugBunny::RemoteError.serialize(exception) if status == 500 && exception
307
+
308
+ error_payload = { status: status, body: body }
298
309
  reply(error_payload, properties.reply_to, properties.correlation_id)
299
310
  end
300
311
 
@@ -165,7 +165,7 @@ module BugBunny
165
165
  current_arounds = resolve_callbacks(self.class.around_actions, action_name)
166
166
 
167
167
  # Definir el núcleo de ejecución
168
- core_execution = lambda do
168
+ core_execution = proc do
169
169
  return unless run_before_actions(action_name)
170
170
 
171
171
  raise NameError, "Action '#{action_name}' not found in #{self.class.name}" unless respond_to?(action_name)
@@ -224,7 +224,12 @@ module BugBunny
224
224
  {
225
225
  status: 500,
226
226
  headers: response_headers,
227
- body: { error: 'Internal Server Error', detail: exception.message, type: exception.class.name }
227
+ body: {
228
+ error: 'Internal Server Error',
229
+ detail: exception.message,
230
+ type: exception.class.name,
231
+ bug_bunny_exception: BugBunny::RemoteError.serialize(exception)
232
+ }
228
233
  }
229
234
  end
230
235
 
@@ -50,6 +50,14 @@ module BugBunny
50
50
  # Pasamos el body crudo; UnprocessableEntity lo procesará en exception.rb
51
51
  raise BugBunny::UnprocessableEntity, body
52
52
  when 500..599
53
+ if body.is_a?(Hash) && body['bug_bunny_exception']
54
+ exception_data = body['bug_bunny_exception']
55
+ raise BugBunny::RemoteError.new(
56
+ exception_data['class'],
57
+ exception_data['message'],
58
+ exception_data['backtrace'] || []
59
+ )
60
+ end
53
61
  raise BugBunny::InternalServerError, format_error_message(body)
54
62
  else
55
63
  handle_unknown_error(status, body)
@@ -89,7 +89,9 @@ module BugBunny
89
89
  BugBunny.configuration.on_rpc_reply&.call(result[:headers])
90
90
 
91
91
  safe_log(:debug, 'producer.rpc_response_received',
92
- messaging_system: 'rabbitmq', messaging_operation: 'receive', messaging_message_id: cid)
92
+ messaging_system: 'rabbitmq', messaging_operation: 'receive', messaging_message_id: cid,
93
+ response_body: result[:body]&.truncate(500),
94
+ response_headers: result[:headers]&.to_json&.truncate(300))
93
95
 
94
96
  parse_response(result[:body])
95
97
  ensure
@@ -125,7 +127,12 @@ module BugBunny
125
127
  safe_log(:info, 'producer.publish', method: verb, path: target, **otel_fields)
126
128
  safe_log(:debug, 'producer.publish_detail', messaging_destination_name: request.exchange,
127
129
  exchange_opts: final_x_opts)
128
- safe_log(:debug, 'producer.publish_payload', payload: payload.truncate(300)) if payload.is_a?(String)
130
+ return unless payload.is_a?(String)
131
+
132
+ safe_log(:info, 'producer.publish_payload',
133
+ payload: payload.truncate(500),
134
+ payload_class: payload.class.name,
135
+ body_size: request.body.nil? ? 0 : request.body.size)
129
136
  end
130
137
 
131
138
  # Serializa el mensaje para su transporte.
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BugBunny
4
+ # Error 500 especial que propagationa información de una excepción remota.
5
+ #
6
+ # Cuando un controller levanta una excepción no manejada en el worker, esta clase
7
+ # permite al llamador RPC acceder a:
8
+ # - La clase original de la excepción (ej: ActiveRecord::RecordNotFound)
9
+ # - El mensaje original
10
+ # - El backtrace completo para debugging
11
+ #
12
+ # Mantiene compatibilidad hacia atrás: si la respuesta no contiene
13
+ # bug_bunny_exception, se comporta como un InternalServerError común.
14
+ class RemoteError < ServerError
15
+ # @return [String] La clase de la excepción remota (ej: 'ActiveRecord::RecordNotFound').
16
+ attr_reader :original_class
17
+
18
+ # @return [String] El mensaje original de la excepción.
19
+ attr_reader :original_message
20
+
21
+ # @return [Array<String>] El backtrace original de la excepción.
22
+ attr_reader :original_backtrace
23
+
24
+ # Serializa una excepción para transmitirse como parte de la respuesta.
25
+ #
26
+ # @param exception [StandardError] La excepción a serializar.
27
+ # @param max_lines [Integer] Máximo de líneas del backtrace (default 25).
28
+ # @return [Hash] Estructura con class, message y backtrace.
29
+ def self.serialize(exception, max_lines: 25)
30
+ {
31
+ class: exception.class.name,
32
+ message: exception.message,
33
+ backtrace: exception.backtrace&.first(max_lines) || []
34
+ }
35
+ end
36
+
37
+ # Inicializa la excepción remota propagada desde el worker.
38
+ #
39
+ # @param original_class [String] Nombre completo de la clase de la excepción.
40
+ # @param message [String] Mensaje de la excepción.
41
+ # @param backtrace [Array<String>] Stack trace completo.
42
+ def initialize(original_class, message, backtrace)
43
+ @original_class = original_class
44
+ @original_message = message
45
+ @original_backtrace = backtrace || []
46
+ super(message)
47
+ set_backtrace(backtrace || [])
48
+ end
49
+
50
+ # @return [String] Representación legible de la excepción.
51
+ def to_s
52
+ "#{self.class.name}(#{original_class}): #{message}"
53
+ end
54
+ end
55
+ end
@@ -401,6 +401,17 @@ module BugBunny
401
401
  attributes['id'] || @extra_attributes['id'] || @extra_attributes['ID'] || @extra_attributes['Id'] || @extra_attributes['_id']
402
402
  end
403
403
 
404
+ # Representación legible del recurso.
405
+ # Muestra solo ID y atributos principales, sin detalles de infraestructura.
406
+ #
407
+ # @return [String]
408
+ def inspect
409
+ infra_keys = %w[routing_key exchange exchange_type exchange_options queue_options _id]
410
+ attrs = @extra_attributes.merge(attributes).reject { |k, _| infra_keys.include?(k) || k == 'id' }
411
+ attr_str = attrs.first(5).map { |k, v| "#{k}=#{v.inspect}" }.join(' ')
412
+ "#<#{self.class.name} id=#{id.inspect} persisted=#{@persisted}#{" #{attr_str}" unless attr_str.empty?}>"
413
+ end
414
+
404
415
  def id=(value)
405
416
  if self.class.attribute_names.include?('id')
406
417
  super
@@ -461,6 +472,7 @@ module BugBunny
461
472
  # @return [Boolean]
462
473
  def destroy
463
474
  return false unless persisted?
475
+ return false unless id
464
476
 
465
477
  run_callbacks(:destroy) do
466
478
  path = "#{self.class.resource_name}/#{id}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BugBunny
4
- VERSION = '4.9.0'
4
+ VERSION = '4.10.0'
5
5
  end
data/lib/bug_bunny.rb CHANGED
@@ -4,6 +4,7 @@ require 'bunny'
4
4
  require 'logger'
5
5
  require_relative 'bug_bunny/version'
6
6
  require_relative 'bug_bunny/exception'
7
+ require_relative 'bug_bunny/remote_error'
7
8
  require_relative 'bug_bunny/configuration'
8
9
  require_relative 'bug_bunny/observability'
9
10
  require_relative 'bug_bunny/otel'
data/skills.lock CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- synced_at: '2026-04-05 13:40:39'
2
+ synced_at: '2026-04-08 09:13:30'
3
3
  skills:
4
4
  - name: agent-review
5
5
  scope: local
@@ -19,6 +19,9 @@ skills:
19
19
  - name: quality-code
20
20
  scope: local
21
21
  path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/quality-code"
22
+ - name: rabbitmq-expert
23
+ scope: local
24
+ path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/rabbitmq-expert"
22
25
  - name: skill-builder
23
26
  scope: local
24
27
  path: "/Users/gabriel/src/gems/bug_bunny/.agents/skills/skill-builder"
data/skills.yml CHANGED
@@ -38,3 +38,7 @@ skills:
38
38
  repo: vercel-labs/skills
39
39
  path: skills/find-skills
40
40
  scope: local
41
+ rabbitmq-expert:
42
+ repo: martinholovsky/claude-skills-generator
43
+ path: skills/rabbitmq-expert
44
+ scope: local
@@ -110,4 +110,48 @@ RSpec.describe BugBunny::Resource, :integration do
110
110
  end
111
111
  end
112
112
  end
113
+
114
+ describe '#inspect' do
115
+ let(:node) { SpecNode.new(id: '123', name: 'test-node', status: 'active', ip: '192.168.1.1', port: 8080) }
116
+
117
+ it 'muestra id y persisted' do
118
+ expect(node.inspect).to include('id="123"')
119
+ expect(node.inspect).to include('persisted=false')
120
+ end
121
+
122
+ it 'muestra atributos principales sin detalles de infraestructura' do
123
+ result = node.inspect
124
+
125
+ expect(result).to include('name="test-node"')
126
+ expect(result).to include('status="active"')
127
+ expect(result).to include('ip="192.168.1.1"')
128
+ expect(result).to include('port=8080')
129
+ expect(result).not_to include('routing_key')
130
+ expect(result).not_to include('exchange')
131
+ end
132
+
133
+ it 'filtra atributos de infraestructura cuando están presentes' do
134
+ node.routing_key = 'radius_1'
135
+ node.exchange = 'test_exchange'
136
+ node.exchange_type = 'topic'
137
+ node.exchange_options = { durable: true }
138
+ node.persisted = true
139
+
140
+ result = node.inspect
141
+
142
+ expect(result).not_to include('routing_key')
143
+ expect(result).not_to include('exchange')
144
+ expect(result).not_to include('exchange_type')
145
+ expect(result).not_to include('exchange_options')
146
+ expect(result).to include('persisted=true')
147
+ end
148
+
149
+ it 'limita a 5 atributos principales' do
150
+ node = SpecNode.new(id: '1', a: '1', b: '2', c: '3', d: '4', e: '5', f: '6')
151
+
152
+ result = node.inspect
153
+
154
+ expect(result.scan('=').length).to be <= 7 # id + persisted + max 5 attrs
155
+ end
156
+ end
113
157
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bug_bunny
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.9.0
4
+ version: 4.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gabix
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-06 00:00:00.000000000 Z
11
+ date: 2026-04-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -229,17 +229,6 @@ executables: []
229
229
  extensions: []
230
230
  extra_rdoc_files: []
231
231
  files:
232
- - ".agents/skills/documentation-writer/SKILL.md"
233
- - ".agents/skills/gem-release/SKILL.md"
234
- - ".agents/skills/quality-code/SKILL.md"
235
- - ".agents/skills/sentry/SKILL.md"
236
- - ".agents/skills/sentry/references/api-endpoints.md"
237
- - ".agents/skills/sentry/scripts/sentry.rb"
238
- - ".agents/skills/skill-builder/SKILL.md"
239
- - ".agents/skills/skill-manager/SKILL.md"
240
- - ".agents/skills/skill-manager/scripts/sync.rb"
241
- - ".agents/skills/yard/SKILL.md"
242
- - ".agents/skills/yard/references/tipos.md"
243
232
  - CHANGELOG.md
244
233
  - CLAUDE.md
245
234
  - README.md
@@ -260,6 +249,7 @@ files:
260
249
  - lib/bug_bunny/otel.rb
261
250
  - lib/bug_bunny/producer.rb
262
251
  - lib/bug_bunny/railtie.rb
252
+ - lib/bug_bunny/remote_error.rb
263
253
  - lib/bug_bunny/request.rb
264
254
  - lib/bug_bunny/resource.rb
265
255
  - lib/bug_bunny/routing/route.rb
@@ -311,7 +301,7 @@ metadata:
311
301
  homepage_uri: https://github.com/gedera/bug_bunny
312
302
  source_code_uri: https://github.com/gedera/bug_bunny
313
303
  changelog_uri: https://github.com/gedera/bug_bunny/blob/main/CHANGELOG.md
314
- documentation_uri: https://github.com/gedera/bug_bunny/blob/v4.9.0/skill
304
+ documentation_uri: https://github.com/gedera/bug_bunny/blob/v4.10.0/skill
315
305
  post_install_message:
316
306
  rdoc_options: []
317
307
  require_paths:
@@ -1,45 +0,0 @@
1
- ---
2
- name: documentation-writer
3
- description: 'Diátaxis Documentation Expert. An expert technical writer specializing in creating high-quality software documentation, guided by the principles and structure of the Diátaxis technical documentation authoring framework.'
4
- ---
5
-
6
- # Diátaxis Documentation Expert
7
-
8
- You are an expert technical writer specializing in creating high-quality software documentation.
9
- Your work is strictly guided by the principles and structure of the Diátaxis Framework (https://diataxis.fr/).
10
-
11
- ## GUIDING PRINCIPLES
12
-
13
- 1. **Clarity:** Write in simple, clear, and unambiguous language.
14
- 2. **Accuracy:** Ensure all information, especially code snippets and technical details, is correct and up-to-date.
15
- 3. **User-Centricity:** Always prioritize the user's goal. Every document must help a specific user achieve a specific task.
16
- 4. **Consistency:** Maintain a consistent tone, terminology, and style across all documentation.
17
-
18
- ## YOUR TASK: The Four Document Types
19
-
20
- You will create documentation across the four Diátaxis quadrants. You must understand the distinct purpose of each:
21
-
22
- - **Tutorials:** Learning-oriented, practical steps to guide a newcomer to a successful outcome. A lesson.
23
- - **How-to Guides:** Problem-oriented, steps to solve a specific problem. A recipe.
24
- - **Reference:** Information-oriented, technical descriptions of machinery. A dictionary.
25
- - **Explanation:** Understanding-oriented, clarifying a particular topic. A discussion.
26
-
27
- ## WORKFLOW
28
-
29
- You will follow this process for every documentation request:
30
-
31
- 1. **Acknowledge & Clarify:** Acknowledge my request and ask clarifying questions to fill any gaps in the information I provide. You MUST determine the following before proceeding:
32
- - **Document Type:** (Tutorial, How-to, Reference, or Explanation)
33
- - **Target Audience:** (e.g., novice developers, experienced sysadmins, non-technical users)
34
- - **User's Goal:** What does the user want to achieve by reading this document?
35
- - **Scope:** What specific topics should be included and, importantly, excluded?
36
-
37
- 2. **Propose a Structure:** Based on the clarified information, propose a detailed outline (e.g., a table of contents with brief descriptions) for the document. Await my approval before writing the full content.
38
-
39
- 3. **Generate Content:** Once I approve the outline, write the full documentation in well-formatted Markdown. Adhere to all guiding principles.
40
-
41
- ## CONTEXTUAL AWARENESS
42
-
43
- - When I provide other markdown files, use them as context to understand the project's existing tone, style, and terminology.
44
- - DO NOT copy content from them unless I explicitly ask you to.
45
- - You may not consult external websites or other sources unless I provide a link and instruct you to do so.
@@ -1,116 +0,0 @@
1
- ---
2
- name: gem-release
3
- description: Automatiza el proceso de liberación (release) de gemas Ruby siguiendo estándares de industria. Úsala cuando necesites subir una nueva versión a RubyGems. Ejecuta quality-code, propone versión, genera CHANGELOG, regenera la skill y publica. Soporta [patch|minor|major].
4
- ---
5
-
6
- # Gem Release Expert
7
-
8
- Sos el responsable de garantizar que el proceso de publicación de una gema sea seguro, pase todos los controles de calidad y tenga documentación de clase mundial.
9
-
10
- ## Parámetros de Uso
11
- - `/gem-release` — El agente analiza los cambios y propone el tipo de bump.
12
- - `/gem-release patch|minor|major` — Override manual del tipo de bump.
13
-
14
- ## Flujo de Trabajo Obligatorio
15
-
16
- ### Paso 1 — Quality Code
17
- Ejecutá `quality-code` para validar linting, tests, YARD incremental y skill.
18
-
19
- ### Paso 2 — Propuesta de Versión
20
- No asumas rutas fijas. Investigá el entorno:
21
- - Detectá el nombre de la gema del `.gemspec`.
22
- - Localizá el archivo de versión (`lib/**/version.rb`).
23
- - **Versión actual:** Obtené el último tag publicado en el remoto (`git tag --sort=-v:refname | head -1` o `git ls-remote --tags origin`). El tag remoto es la fuente de verdad — NO leer `version.rb` para determinar la versión actual, ya que puede estar modificado localmente.
24
- - **Análisis de cambios:** Revisá **todas** las fuentes de cambios:
25
- 1. Commits desde el último tag: `git log [último-tag]...HEAD`
26
- 2. Diff commiteado contra el tag: `git diff [último-tag]...HEAD`
27
- 3. Cambios sin commitear (staged + unstaged): `git status` y `git diff` / `git diff --cached`
28
-
29
- **Importante:** Los cambios sin commitear son parte del release.
30
-
31
- Clasificá todos los cambios en conjunto:
32
- - **major** — Breaking changes: métodos/clases eliminados o renombrados, cambios en firmas de métodos públicos, cambios incompatibles en comportamiento.
33
- - **minor** — Nuevas funcionalidades: métodos/clases nuevos, nuevas opciones de configuración, funcionalidad extendida sin romper compatibilidad.
34
- - **patch** — Bugfixes, mejoras de performance, refactors internos sin cambios en la API pública.
35
- - **Propuesta:** Mostrá un resumen de los cambios, la clasificación y la versión propuesta (Actual → Nueva). Esperá confirmación.
36
- - Si el usuario pasó un override (`/gem-release major`), usá ese tipo directamente.
37
-
38
- ### Paso 3 — Documentación y Skill
39
- 1. **Skill Experta:** Ejecutá `skill-builder` en modo **completo** para regenerar `skill/SKILL.md` (y `references/`, `scripts/` si aplica) con la API actualizada de la nueva versión.
40
- 2. **Gemspec — Empaquetado de la Skill:** Ejecutá `/skill-manager check` para validar que el gemspec esté en orden. Verificá que `skill/` esté incluido en `spec.files`.
41
- - Si `spec.files` usa `git ls-files`, asegurate de que `skill/` esté commiteado.
42
- - Si `spec.files` usa un glob explícito, asegurate de que incluya `skill/**/*`.
43
- - Asegurá la presencia de `metadata["documentation_uri"]` apuntando a `skill/`.
44
-
45
- ### Paso 4 — Aplicación del Release
46
- 1. Actualizá el archivo `version.rb` con la Nueva Versión.
47
- 2. **Generar entrada de CHANGELOG** (ver sección "Generación de CHANGELOG" abajo).
48
- 3. **Persistencia Git:**
49
- - `git add .`
50
- - `git commit -m "release: v[NUEVA_VERSION]"`
51
- - `git tag -a v[NUEVA_VERSION] -m "Version [NUEVA_VERSION]"`
52
-
53
- ### Paso 5 — Push (Requiere confirmación)
54
- Mostrá un resumen del commit y el tag creados. Esperá confirmación explícita antes de pushear.
55
- - `git push origin main`
56
- - `git push origin v[NUEVA_VERSION]`
57
-
58
- **Nota:** No es necesario hacer `gem build` ni `gem push` manualmente. Un GitHub Action se encarga de buildear y publicar la gema en RubyGems cuando detecta el tag.
59
-
60
- ---
61
-
62
- ## Generación de CHANGELOG
63
-
64
- ### Fuente de datos
65
- Leer todos los commits desde el último tag: `git log [último-tag]...HEAD --format="%H %s (%an)"`
66
-
67
- ### Filtrado
68
- Ignorar commits que matcheen:
69
- - `release:` — commits de release anteriores
70
- - `Merge branch` / `Merge pull request` — merges automáticos
71
- - `chore:` — tareas de mantenimiento sin impacto funcional
72
-
73
- ### Agrupación por tipo
74
- Clasificar cada commit por su prefijo conventional commit:
75
-
76
- ```markdown
77
- ## [X.X.X] — YYYY-MM-DD
78
-
79
- ### Nuevas funcionalidades
80
- - Agregar endpoint de facturación (#123) — @dev1
81
- - Soportar filtro por fecha en listado — @dev2
82
-
83
- ### Correcciones
84
- - Corregir cálculo de IVA en pagos parciales — @dev1
85
- - Resolver timeout en conexión a Redis — @dev3
86
-
87
- ### Mejoras internas
88
- - Extraer servicio de notificaciones — @dev2
89
- - Optimizar queries de reportes — @dev1
90
- ```
91
-
92
- **Mapeo de prefijos:**
93
- - `feat:` → **Nuevas funcionalidades**
94
- - `fix:` → **Correcciones**
95
- - `refactor:`, `perf:`, `style:` → **Mejoras internas**
96
- - `docs:` → **Documentación**
97
- - `test:` → **Tests**
98
- - Sin prefijo reconocido → **Otros cambios**
99
-
100
- ### Formato
101
- - Cada entrada: descripción limpia (sin el prefijo), PR o issue si está en el mensaje, autor (`@nombre`).
102
- - Fecha en formato `YYYY-MM-DD`.
103
- - Si el `CHANGELOG.md` no existe, crearlo con header `# Changelog`.
104
- - La entrada nueva va al tope del archivo, debajo del header.
105
-
106
- ### Atribución
107
- Extraer el autor de cada commit con `git log --format="%an"`. Esto permite saber qué dev contribuyó a cada cambio en el release.
108
-
109
- ---
110
-
111
- ## Reglas de Seguridad y Estilo
112
- - **Tag con `v`:** Los tags de gemas usan formato `vX.X.X`.
113
- - **Stop on Failure:** Si quality-code falla, detenete. No fuerces el release.
114
- - **Confirmation:** Siempre esperá confirmación antes de persistir cambios en Git y publicar.
115
- - **Sin Hardcoding:** No uses versiones de Ruby o rutas específicas. Confiá en el entorno configurado.
116
- - **CHANGELOG is Law:** Todo release debe tener su entrada en CHANGELOG.md.
@@ -1,51 +0,0 @@
1
- ---
2
- name: quality-code
3
- description: Validación de calidad para proyectos Ruby (gemas y servicios). Ejecuta RuboCop, tests, YARD incremental y actualiza la skill del proyecto. Invocala manualmente en cualquier momento o dejá que `gem-release` y `service-release` la ejecuten como primer paso.
4
- ---
5
-
6
- # Quality Code
7
-
8
- Barrera de calidad para proyectos Ruby. Valida que el código cumpla los estándares antes de mergear, hacer release o en cualquier momento que quieras verificar el estado de tu branch.
9
-
10
- ## Detección de tipo de proyecto
11
-
12
- - **Gema**: existe `.gemspec` en la raíz.
13
- - **Servicio**: existe `config/application.rb`.
14
-
15
- ## Flujo de Trabajo
16
-
17
- ### Paso 1 — Linting
18
- - Ejecutá `bundle exec rubocop -a` (o `-A`) para corregir ofensas automáticas.
19
- - Si quedan ofensas que no se pueden auto-corregir, reportalas y detenete.
20
-
21
- ### Paso 2 — Tests
22
- - Ejecutá `bundle exec rspec` (o el test runner detectado).
23
- - Si un test falla, el proceso se detiene inmediatamente.
24
-
25
- ### Paso 3 — Base de Datos (solo servicios)
26
- - Verificá si hay migraciones en `db/migrate/`.
27
- - Asegurate de que `db/schema.rb` esté actualizado y consistente.
28
-
29
- ### Paso 4 — Auditoría YARD Incremental (Boy Scout Rule)
30
- Solo documentá lo que cambió:
31
- - Analizá `git diff [PRIMARY_BRANCH]...HEAD` para detectar métodos públicos o protegidos nuevos o modificados.
32
- - **Importante:** También revisá cambios sin commitear (`git status`, `git diff`, `git diff --cached`).
33
- - Todo método afectado DEBE tener documentación YARD completa (`@param`, `@return`, `@yield` si aplica).
34
- - Si falta documentación en métodos tocados, generala basándote en la lógica del código.
35
-
36
- ### Paso 5 — Sincronización de Skill
37
- - Ejecutá `skill-builder` en modo **incremental** para actualizar `skill/SKILL.md` (y `references/`, `scripts/` si aplica) con los cambios actuales.
38
- - Auditá si el `README.md` necesita actualizarse por los cambios.
39
-
40
- ### Paso 6 — Reporte
41
- Mostrá un resumen de lo ejecutado:
42
- - RuboCop: OK / X ofensas corregidas
43
- - Tests: OK / X tests, X failures
44
- - YARD: OK / X métodos documentados
45
- - Skill: actualizada / sin cambios
46
- - README: actualizado / sin cambios
47
-
48
- ## Reglas
49
- - **Stop on Failure:** Si los tests o el linting fallan, detenete. No continues con los pasos siguientes.
50
- - **Sin Hardcoding:** No uses versiones de Ruby o rutas específicas. Confiá en el entorno configurado.
51
- - **Explain the Why:** Si sugerís cambios en documentación, explicá cómo mejora la mantenibilidad.