Ejecución de flujo de trabajo con una aprobación, un nodo de retardo y variables de ejecución

Semaphore UI 2.20 añade un registro de auditoría con exportación a SIEM, convierte los Flujos de trabajo en un verdadero motor de pipelines con nodos de retardo, revisiones, salidas transmitidas entre tareas y variables de flujo de trabajo, rehace los roles para que un miembro pueda tener varios y los administradores dispongan de roles de alcance global y asignaciones de grupos del IdP, introduce la configuración de hosts para credenciales SSH y Git por host, traslada todas las variables adicionales de Ansible a archivos cifrados con Vault y expone métricas de Prometheus.

La línea comienza en v2.20.0. Las versiones de parche se enumerarán al final de esta página a medida que se publiquen.

Aspectos destacados

  • Registro de auditoría — quién hizo qué, desde dónde y con qué resultado; una pantalla de administración, retención y exportación a Syslog / Splunk HEC para SIEM (la exportación y los filtros, en Pro).
  • Flujos de trabajo — nodos de retardo, revisiones inmutables fijadas a las ejecuciones, salidas entregadas como entradas de la tarea siguiente, variables de flujo de trabajo solicitadas en un diálogo de ejecución, un editor rediseñado con atajos de teclado y deshacer, y webhooks que inician flujos de trabajo.
  • Varios roles por miembro, roles con ids y slugs, roles globales con permisos de alcance global (Pro) y asignaciones grupo del IdP → rol aplicadas al iniciar sesión (Enterprise).
  • Identidades externas — los inicios de sesión LDAP y OIDC se vinculan a las cuentas por el id estable del proveedor, no por correo electrónico; varios directorios LDAP en una sola página de inicio de sesión.
  • Configuración de hosts — asigna hosts SSH y URLs de Git a credenciales del proyecto, aplicadas a los clones, Galaxy, los módulos de Terraform y Ansible.
  • Las variables adicionales de Ansible ya nunca van en la línea de comandos; los secretos viajan en un archivo cifrado con Ansible Vault y Jinja2 dentro de los valores ya no se evalúa.
  • Plantillas: directorio de trabajo de Ansible, nombres únicos y template_name en la API de tareas, notificaciones de error silenciadas, Dry Run / Diff ocultos, opciones de instalación de Galaxy, variables de encuesta de selección múltiple, secretos de encuesta en runners remotos.
  • Repositorios monorepo comparten un único checkout entre plantillas; reintentos de Git, obtención paralela de submódulos, comprobación de claves de host SSH.
  • Métricas de Prometheus en /api/metrics.
  • Generación de pares de claves SSH en el servidor y copia de la clave pública desde el almacén de claves.

Registro de auditoría

Pantalla del registro de auditoría

Semaphore registra ahora los eventos relevantes para la seguridad en un log dedicado y de solo adición. Actívalo con:

"audit": {
  "enabled": true,
  "instance_id": "semaphore-prod"
}

(SEMAPHORE_AUDIT_ENABLED, SEMAPHORE_AUDIT_INSTANCE_ID; instance_id es obligatorio y nombra la instalación en cada evento.)

Qué se registra. 116 tipos de evento en ocho categorías: auth (inicios y cierres de sesión, MFA, rechazos de tokens de API, peticiones denegadas, bloqueos CSRF), iam (usuarios, contraseñas, tokens de API, identidades, pertenencias, roles, permisos de plantilla), resource (proyectos, copias de seguridad, inventarios, repositorios, plantillas, programaciones, integraciones, configuraciones de host, flujos de trabajo, grupos de variables), secret (credenciales y almacenamientos de secretos), task (inicio con su disparador, finalización con resultado y duración, aprobaciones, detenciones, eliminación del historial), runner (registro, cambios, informes rechazados), system (ajustes, licencia) y audit en sí mismo (inicio, retención, exportación). Las contraseñas, los tokens, los valores de secretos, la salida de las tareas y las variables nunca se escriben.

Envoltorio del evento. Cada evento lleva un UUID, un número de secuencia sin huecos, una marca de tiempo UTC con precisión de microsegundos, el actor (usuario / anónimo / sistema / runner / integración, con autenticación por sesión o token de API y una huella del token), la IP de origen y el User‑Agent, el objetivo, el proyecto, un id de petición (devuelto también en la cabecera de respuesta X-Request-ID), el id de la instancia y el id del nodo HA. Los resultados son success o failure con una lista cerrada de motivos; una acción que guardó el objeto pero falló en un paso posterior es un éxito con partial = true. Detrás de un proxy inverso, X-Forwarded-For solo se considera de confianza desde audit.trusted_proxy_cidrs.

Tarjeta de evento de auditoría

Pantalla de administración. Registro de auditoría en el menú de usuario (/audit-log) enumera los eventos del más reciente al más antiguo, 50 por página, con paginación Más antiguos / Más recientes; una fila abre una tarjeta con las secciones Quién, Objeto, Dónde, Cuándo, Detalles y Técnico y un botón que copia el evento como el JSON enviado a un SIEM. El acceso requiere el nuevo permiso global Leer el registro de auditoría.

Retención. audit.retention_days elimina los eventos más antiguos por lotes, cada hora y con jitter para que los nodos HA no colisionen; cada pasada que elimina algo queda a su vez registrada.

Pro: filtros y exportación. Filtra por periodo, usuario, proyecto, tipo de evento, resultado e IP; pivota desde cualquier valor de la tarjeta; exporta la selección actual como CSV o JSON Lines.

Pro: exportación a SIEM. Dos destinos, cada uno con su propio cursor para que nada se omita cuando un receptor está caído: Syslog sobre TLS (RFC 5424, el JSON del evento como cuerpo) mediante audit.syslog y Splunk HTTP Event Collector mediante audit.splunk_hec (url, token, index, source, sourcetype, ca_file, …). La entrega es «al menos una vez», ordenada por secuencia; en HA solo un nodo exporta por destino, coordinado mediante una concesión en Redis. Tres series de Prometheus describen el exportador: semaphore_audit_export_pending_events, semaphore_audit_export_oldest_pending_seconds y semaphore_audit_export_errors_total.

Almacenamiento: tablas audit_event, audit_event_sequence, audit_export_state (migraciones v2.20.9 a v2.20.11). API: GET /api/audit/events y GET /api/audit/events/export.


Flujos de trabajo

Los flujos de trabajo son una función Pro; la etiqueta Beta ha desaparecido.

Nodos de retardo

Un cuarto tipo de nodo junto a tarea, aprobación y nota: Retardo pausa la ejecución durante un número de segundos y después libera sus aristas. La vista de ejecución muestra una cuenta atrás en vivo en la tarjeta; una ejecución detenida cancela el retardo. Campo delay_seconds, migración v2.20.2.

Revisiones

Cada guardado de un flujo de trabajo crea una nueva revisión del grafo; una ejecución fija la revisión con la que empezó, de modo que editar un flujo de trabajo nunca altera una ejecución en curso ni deja huérfanos los nodos de ejecuciones pasadas. El editor muestra una etiqueta rev. N y cuántas ejecuciones siguen ejecutando una revisión anterior; la vista de ejecución muestra qué revisión ejecuta. Las revisiones que ninguna ejecución referencia se eliminan cuando se guarda una más reciente. API: GET …/workflows/{id}/revisions y …/revisions/{revision_id}.

Salidas y entradas

Vista de ejecución con insignias de salidas

Una tarea puede entregar datos estructurados a los nodos posteriores. Cada tarea iniciada por un flujo de trabajo recibe SEMAPHORE_OUTPUTS_FILE; lo que escriba allí como objeto JSON se convierte en sus salidas:

cat > "$SEMAPHORE_OUTPUTS_FILE" <<EOF
{"image_tag": "1.4.2", "replicas": 3, "subnet_ids": ["subnet-1", "subnet-2"]}
EOF

Ansible produce salidas con ansible.builtin.set_stats a través de un plugin de callback incluido; las salidas de Terraform, OpenTofu y Terragrunt se capturan automáticamente de output -json (los valores sensitive se omiten y se listan como No capturado). Las salidas solo se leen cuando la tarea termina con éxito; los límites son 100 salidas, 32 KB cada una y 256 KB por archivo.

Una conexión hacia un nodo de tarea entrega entradas — rellena las variables de encuesta de la plantilla de destino con las salidas del nodo de origen:

  • Por nombre (predeterminado): cada salida cuyo nombre coincide con una variable de encuesta la rellena.
  • Asignar entradas explícitamente: enumera los pares variable ← output key que se entregan; una vez que el flujo de trabajo tiene una ejecución finalizada, el panel sugiere las claves que produjo esa ejecución.

Panel de conexión con asignación explícita de entradas

Los valores se convierten al tipo de la variable, los nodos de aprobación y de retardo transmiten las salidas, una asignación explícita prevalece sobre la entrega por nombre y una variable obligatoria que queda sin valor hace fallar la tarea del nodo antes de que empiece, de modo que la ejecución sigue sus aristas en caso de fallo. La vista de ejecución muestra una insignia N salidas por nodo, el diálogo de tarea incluye una tabla Salidas y el log de la tarea empieza con una línea Input "x" <- output "y" of node N por cada valor entregado. Las salidas se almacenan en texto plano y nunca rellenan una variable secret; un secret no puede transmitirse mediante salidas. Las tareas en runners remotos producen y reciben salidas; los ejecutores Docker y Kubernetes todavía no las capturan.

Diálogo de tarea con la tabla Salidas

Variables de flujo de trabajo y el diálogo de ejecución

Diálogo de ejecución solicitando las variables del flujo de trabajo

Un flujo de trabajo tiene sus propias variables de encuesta, solicitadas una sola vez en un diálogo Ejecutar “…” al iniciar una ejecución (un flujo de trabajo sin variables se inicia de inmediato, como antes). Las respuestas se almacenan en la ejecución, se muestran como chips en una franja Variables sobre el lienzo de la ejecución y llegan a una tarea solo mediante un enlace explícito en el panel del nodo — una variable de flujo de trabajo nunca se filtra a una plantilla por nombre. Los secretos no se admiten como variables de flujo de trabajo. POST …/workflows/{id}/runs acepta {"environment": {"image_tag": "1.4.3"}}; una respuesta que no encaja con su tipo o un valor obligatorio que falta rechaza el inicio.

Entorno de una tarea de flujo de trabajo

Cada tarea iniciada por un flujo de trabajo recibe SEMAPHORE_WORKFLOW_ID, SEMAPHORE_WORKFLOW_RUN_ID, SEMAPHORE_WORKFLOW_URL (necesita web_host) y SEMAPHORE_OUTPUTS_FILE, para todas las aplicaciones y también en runners remotos.

Editor rediseñado

Editor de flujos de trabajo

  • Nuevas tarjetas de nodo con mosaico de tipo, estado, duración, insignia de salidas y Aprobar / Rechazar en línea; etiquetas de arista con un desplegable de condición in situ y un botón de eliminar; un lienzo con cuadrícula de puntos.
  • Menú de adición rápida desde el conector + de un nodo, con clic derecho en el lienzo o con el botón del estado vacío: lista buscable de tipos de nodo y plantillas de tarea, el nuevo nodo se conecta en caso de éxito.
  • Deshacer / rehacer (Ctrl/⌘+Z, Ctrl/⌘+Shift+Z), renombrado en línea del flujo de trabajo, un menú Problemas que enfoca el nodo infractor, una insignia Guardar para los cambios sin guardar y una protección al salir de la página.
  • Atajos de teclado en el lienzo: las flechas desplazan (Shift para un paso grande), + / - hacen zoom, 0 ajusta el grafo, 1 restablece el zoom, Esc borra la selección; las tarjetas de nodo admiten foco por teclado.
  • Controles flotantes: acercar / alejar, ajustar vista, autodisposición Ordenar, porcentaje de zoom.
  • Un solo chevrón contrae la paleta y la navegación principal a la vez; el estado se recuerda.
  • El desbordamiento de desplazamiento del trackpad ya no activa el gesto Atrás del navegador en macOS.

Menú de adición rápida

Los webhooks inician flujos de trabajo

Una integración puede ahora ejecutar un flujo de trabajo en lugar de una plantilla (Ejecuta: Plantilla / Flujo de trabajo en el formulario de integración). Cada valor extraído se convierte en una respuesta a la encuesta del flujo de trabajo. Gestionar esas integraciones requiere la función Pro workflow_integrations_management. En las integraciones de plantilla, un valor extraído de Parámetros de tarea llamado git_branch establece la rama de la tarea iniciada cuando la plantilla permite sobrescribirla.

Cambios en la API

POST …/workflows/{id}/run se renombró a POST …/workflows/{id}/runs y acepta el cuerpo environment. Los detalles de la ejecución llevan revision_id, revision, un objeto delay por nodo y task.artifacts. Los workflow_run_id, workflow_node_id y artifacts suministrados por el cliente en una tarea se ignoran, de modo que una tarea no puede hacerse pasar por un nodo de la ejecución de otro.


Roles y control de acceso

Varios roles por miembro

Página del equipo con varios roles por miembro

Un miembro del proyecto tiene un conjunto de roles, editado como chips en la página Equipo. La API acepta roles: ["manager", "release_approver"] junto al role heredado; la respuesta conserva el role escalar solo para los miembros con un único rol. Un usuario que no es administrador no puede quitar el rol integrado Owner al último propietario.

Roles con ids, slugs y alcances

Los roles son ahora filas con un id entero y un slug (^[A-Za-z0-9_-]{1,100}$, derivado del nombre en el formulario); los cuatro roles integrados también son filas y ya no pueden ser enmascarados por un rol personalizado. Los roles tienen un alcance: integrado, compartido (utilizable en cualquier proyecto) o de proyecto. Eliminar un rol en uso responde 409 con su impacto (miembros, proyectos, permisos de plantilla, asignaciones) y puede repetirse con ?force=true — Eliminar con asignaciones en la interfaz. Las migraciones v2.20.12 y v2.20.13 reconstruyen las tablas de roles; consulta las notas de actualización.

Roles y permisos globales (Pro)

Pestaña de roles globales de la página Usuarios

Los derechos de alcance global ya no se reducen al indicador de administrador. Un rol global concede una máscara de cuatro permisos — Gestionar usuarios, Gestionar roles globales, Gestionar ajustes del sistema, Leer el registro de auditoría — y se asigna a los usuarios en la pestaña Roles de la página Usuarios (/users/roles) y en la pestaña Roles globales del diálogo de usuario, que muestra además los permisos efectivos y qué rol concede cada uno. Toda ruta de administración está protegida por uno de los cuatro bits; GET /api/user devuelve global_permissions. Una instancia conserva siempre al menos un administrador, garantizado con un bloqueo de fila seguro entre nodos HA. Un usuario con Gestionar usuarios no puede editar a un administrador ni a nadie que tenga un permiso del que él carece. API: /api/global-roles, /api/users/{id}/global-roles, /api/users/{id}/global-permissions, catálogo en /api/global-roles/permissions.

Roles globales de un usuario

Asignaciones de roles por grupo (Enterprise)

Página de asignaciones de grupo

Asigna un grupo del proveedor de identidad a un rol de proyecto en la nueva página Asignaciones de grupo (/role-mappings): proveedor, grupo, proyecto, rol. En cada inicio de sesión OIDC, los roles asignados del usuario se ajustan a los grupos presentes en el token (groups_claim del proveedor). Los roles asignados aparecen en la página Equipo como chips bloqueados con un tooltip provider: group y no pueden eliminarse como roles de miembro; los roles asignados no cuentan como el último propietario del proyecto. Un inicio de sesión con contraseña o LDAP borra los roles asignados, porque no aporta grupos. API: /api/role_mappings.


Autenticación e identidades

Página de inicio de sesión con varios directorios LDAP

  • Identidades externas. Los inicios de sesión LDAP y OIDC se vinculan a las cuentas de Semaphore por el id estable del proveedor (sub de OIDC, DN de LDAP) en la nueva tabla user__external_identity, no por correo electrónico. Esto cierra la clase de fallo en la que un IdP que acepta cualquier dirección escrita adopta la cuenta de otra persona. external_auth_email_matching (auto por defecto, always, never) controla si un usuario externo existente sin identidad puede aún emparejarse una vez por correo electrónico; las cuentas locales con contraseña nunca se adoptan. El correo electrónico de OIDC solo cuenta cuando está email_verified y el proveedor establece require_verified_email. Los usuarios vinculan y desvinculan identidades por sí mismos en el diálogo de cuenta (/api/users/{id}/identities, POST /api/user/identities/ldap, /api/auth/oidc/{provider}/login?link=1).
  • Varios directorios LDAP. ldap_providers es un mapa de directorios con nombre, cada uno con su propio servidor, bind, búsqueda, mapeos, color y orden; el bloque plano heredado ldap_* sigue funcionando como el proveedor ldap. La página de inicio de sesión muestra un selector cuando hay más de una pestaña, y la pestaña interna nunca envía una contraseña a un directorio. GET /api/auth/login devuelve ldap_providers y login_with_password.
  • Los certificados LDAPS se verifican por defecto. Define ldap_tls_skip_verify (bloque plano) o tls_skip_verify (por proveedor) para un certificado autofirmado.
  • Vida absoluta de la sesión. auth.max_session_life_hours (SEMAPHORE_AUTH_MAX_SESSION_LIFE_HOURS) caduca una sesión un tiempo fijo después del inicio de sesión, con independencia de su actividad; 0 conserva solo el tiempo de espera por inactividad.
  • Un usuario sin proyectos que no puede crear ninguno ve una página Todavía no hay proyectos en lugar de un formulario que no puede enviar; POST /api/projects responde 403 en ese caso.
  • El idioma elegido en el menú de usuario se almacena en el servidor y acompaña al usuario entre navegadores (lang en /api/user/options).

Configuración de hosts

Página de configuración de hosts

Una nueva página de proyecto, Configuración de hosts, asigna hosts SSH y URLs de Git a credenciales del proyecto:

  • una asignación de tipo Host (bastion.example.com) adjunta una clave SSH, como un bloque Host de un ssh_config generado;
  • una asignación de tipo URL (https://github.com/acme/, un prefijo terminado en / cubre un grupo) adjunta una clave SSH o un usuario/contraseña (solo HTTPS), como una reescritura insteadOf de Git; gana la URL más específica.
Formulario de asignación de host

Las asignaciones se aplican al repositorio de la plantilla y sus submódulos, a los roles y colecciones de requirements.yml, a los módulos de terraform init, a los comandos git ejecutados por un playbook, a los repositorios de inventario y a los hosts de inventario, y a la exploración de ramas y playbooks; se envían a los runners remotos. Las claves privadas nunca tocan el disco — un agente SSH por asignación, vinculado con IdentityAgent; las contraseñas pasan por GIT_CONFIG_PARAMETERS y las URLs de repositorio se redactan en los logs de las tareas. Una clave referenciada por una asignación no puede eliminarse. Las asignaciones forman parte de las copias de seguridad del proyecto. Requiere git_client: cmd_git. Tabla project__host_config, migración v2.20.7; API /api/project/{id}/host_configs.


Plantillas y tareas

Formulario de plantilla con directorio de trabajo y opciones de notificación

  • Directorio de trabajo de Ansible. Directorio de trabajo distinto ejecuta ansible-playbook desde un subdirectorio del repositorio; la ruta debe permanecer dentro del checkout (working_directory, migración v2.20.3).

  • Nombres de plantilla únicos por proyecto (migración v2.20.4), y POST /api/project/{id}/tasks acepta template_name en lugar de template_id.

  • Silenciar las notificaciones de error por plantilla, junto al silenciado de éxito ya existente; las alertas por correo electrónico respetan ambos por primera vez (suppress_error_alerts, migración v2.20.5).

  • Ocultar Dry Run / Diff del diálogo de tarea por plantilla; el servidor también deja de añadir --check / --diff para esa plantilla (task_params.hide_dry_run, hide_diff).

  • Opciones de instalación de Galaxy — una sección contraíble con Omitir la instalación de Galaxy, argumentos de CLI separados para ansible-galaxy role install y collection install (una lista de permitidos de flags; --token, --api-key y los flags de ruta se rechazan) y la solicitud de sobrescritura por tarea.

  • El selector de playbooks enumera los subdirectorios a medida que escribes, filtrados por aplicación (.yml para Ansible, directorios de primer nivel para Terraform / OpenTofu / Terragrunt / Pulumi, .sh, .py, .ps1), omite los directorios de la estructura de Ansible y los directorios con punto, está limitado a 30 entradas y explora un clon superficial confinado a la raíz del repositorio.

  • Variables de encuesta de selección múltiple — el nuevo tipo select acepta varios valores, representados como chips en el diálogo de tarea; default_value puede ser un array.

    Diálogo de nueva tarea con una variable de encuesta de selección múltiple
  • Los secretos de encuesta llegan a los runners remotos. Una respuesta de encuesta secret llegaba antes vacía a un runner. Ahora se almacena cifrada como una clave de acceso ligada a la tarea (access_key.task_id, expire_at, migración v2.20.1), se descifra en el nodo HA que atienda al runner, se elimina cuando la tarea termina y se limpia cada hora; está oculta para el almacén de claves, las copias de seguridad y la API de claves.

  • Variables adicionales en YAML. El editor de grupos de variables incorpora un conmutador Tabla | JSON | YAML y un editor a pantalla completa; las conversiones son estrictas (las fechas siguen siendo cadenas, se rechazan las raíces que no son objetos y los números no finitos). El almacenamiento no cambia.

    Editor de grupo de variables en modo YAML

  • La página de la tarea nombra y enlaza la programación o integración que la inició, y marca un origen eliminado como #id (deleted). Las marcas de tiempo del log ya no se seleccionan al copiar un log.

  • Las programaciones validan la expresión en el servidor antes de guardarla, así que se acepta @hourly, y el aviso de zona horaria puede descartarse.

    Formulario de programación
  • Detener una tarea local termina todo su grupo de procesos: SIGTERM, un periodo de gracia de 15 s y después SIGKILL; los procesos ssh o de proveedor huérfanos ya no sobreviven a la tarea. Un plan de Terraform detenido ya no continúa; un plan que sale con 0 ante SIGTERM no se interpreta como éxito.

  • El repository_id del inventario se conserva solo para el tipo file y debe pertenecer al proyecto.

  • Los logs del servidor nombran al usuario en las entradas Task added to queue / Task started.


Secretos y Ansible Vault

  • Las variables adicionales viajan en archivos, no en la línea de comandos. Los grupos de variables, las respuestas de encuesta, las entradas del flujo de trabajo y los detalles de la tarea se escriben en un archivo YAML 0600 pasado como --extra-vars @file. Los secretos — las respuestas de encuesta secret y los secretos de los grupos de variables — van a un segundo archivo cifrado en el formato Ansible Vault 1.1 / AES‑256 por una implementación nativa en Go (no se necesita el binario ansible-vault), desbloqueado con una contraseña de un solo uso respondida al --vault-id <uuid>@prompt de ansible-playbook. Ni los valores ni la contraseña aparecen en ps, y ambos archivos se eliminan cuando la tarea termina.

  • Jinja2 en los valores es dato, no código. Toda cadena que contenga {{, {%, {# o una cabecera #jinja2: se etiqueta como !unsafe, de forma recursiva, así que una respuesta de encuesta como {{ lookup('pipe', 'id') }} se pasa literalmente. Las cadenas sin delimitador se dejan sin etiquetar a propósito: ansible‑core ≥ 2.19 vuelve a resolver los escalares !unsafe, lo que convertiría "2026.08" en un float.

  • Un nuevo espacio de nombres de depuración extra_vars (SEMAPHORE_DEBUG_FILTER=extra_vars) registra cómo se reparten y escriben las variables — solo nombres, archivos y tamaños, nunca valores.

  • Generar un par de claves SSH en el servidor. Generar clave SSH en el almacén de claves crea una clave RSA; la clave pública se guarda junto a la clave, se muestra con Mostrar / Ocultar y un botón de copiar, y se ofrece en un diálogo justo después de la creación (generate_ssh_key).

    Clave con un par de claves SSH generado
  • Una actualización de clave escribe type, secret y la parte pública solo cuando Override está activado, de modo que un cliente no puede alterar la carga útil de una clave sin reemplazar el secreto.

  • La lectura de una credencial desde una variable de entorno o un archivo del servidor bajo dirs.secrets está ahora documentada (pestañas Env / File del formulario del almacén de claves).


Repositorios y Git

Formulario de repositorio con la opción de checkout único
  • Monorepo: un único checkout para todas las plantillas. Una opción del repositorio lo clona una sola vez y lo comparte entre plantillas en lugar de una copia por plantilla; el checkout se bloquea para el fetch y el pull, y las tareas se ejecutan después en paralelo. Las plantillas de un repositorio así no pueden tener una rama propia y sus tareas no pueden sobrescribir la rama ni el commit (monorepo, migración v2.20.11).
  • Reintentos de Git. clone y pull se reintentan con un retardo exponencial limitado a un minuto; git_attempts (SEMAPHORE_GIT_ATTEMPTS, por defecto 4).
  • Submódulos en paralelo. git_submodule_jobs (SEMAPHORE_GIT_SUBMODULE_JOBS, por defecto 4) se pasa como --jobs.
  • Comprobación de claves de host SSH. Nueva sección ssh: config_path, known_hosts_file (SEMAPHORE_SSH_KNOWN_HOSTS_FILE) y strict_host_key_checking — no (por defecto), yes o accept-new — aplicada a Git sobre SSH.
  • La exploración del repositorio usa un clon superficial por rama; las credenciales se codifican en porcentaje para los clones en bare‑metal y se redactan en los logs y en el feed de eventos.

Runners e integraciones

  • runner.check_interval_seconds (SEMAPHORE_RUNNER_CHECK_INTERVAL_SECONDS, por defecto 5) define con qué frecuencia sondea un runner; un conmutador Opciones avanzadas en la página Runners lo incluye en los fragmentos generados de variables de entorno, configuración y Docker (publicado en v2.19.17).

  • runner.connection.timeout_seconds (SEMAPHORE_RUNNER_CONNECTION_TIMEOUT_SECONDS, por defecto 3) limita por separado la conexión, el handshake TLS y las cabeceras de respuesta; las subidas de logs no se cortan.

  • runners.default_global_runners_mode (SEMAPHORE_DEFAULT_GLOBAL_RUNNERS_MODE: disable, prefer, require) sustituye al obsoleto use_remote_runner; runners.registration_token sustituye al runner_registration_token de primer nivel.

  • Diagnósticos de runner del lado del servidor con SEMAPHORE_DEBUG_FILTER=runner: motivos de rechazo de tokens, detección de reinicio y de estado en línea, informes de progreso rechazados, trazado del despacho.

  • Las peticiones unregister de un runner se autentican; el cliente del runner rechaza las redirecciones HTTP.

  • Autenticación de webhooks con HMAC‑SHA512 (hmac-sha512) junto a la existente HMAC‑SHA256; la cabecera de la firma es configurable.

    Formulario de integración con HMAC-SHA512
  • Se rechaza un webhook cuya integración pertenece a otro proyecto.


Observabilidad

  • Métricas de Prometheus. GET /api/metrics tras HTTP Basic Auth con una credencial de servicio estática:

    "metrics": { "enabled": true, "username": "prometheus", "password": "…" }
    

    (SEMAPHORE_METRICS_ENABLED, SEMAPHORE_METRICS_USERNAME, SEMAPHORE_METRICS_PASSWORD.) Series: semaphore_tasks_running, semaphore_tasks_total{status}, los colectores estándar go_* y process_*, y los medidores de exportación de auditoría en Pro.

  • Nuevos espacios de nombres de depuración extra_vars y runner; la exploración de repositorios registra la salida de Git en DEBUG.

  • La exportación y la restauración de las copias de seguridad de proyecto se registran.


Seguridad

  • Vinculación de identidades por id del proveedor en lugar de por correo electrónico; verificación de certificados LDAPS activada por defecto.
  • Variables adicionales fuera de la línea de comandos; secretos cifrados con Vault; Jinja2 en los valores sin evaluar.
  • Los workflow_run_id, workflow_node_id y artifacts falsificados se eliminan de las tareas del cliente.
  • Las rutas de administración están protegidas por permisos globales explícitos en lugar del indicador de administrador; se rechazan los bits de permiso desconocidos; protección contra la escalada de privilegios en la gestión de usuarios.
  • El selector de playbooks está confinado a la raíz del repositorio (OpenRoot), limitado y filtrado.
  • Se validan las raíces de las variables adicionales y los números YAML; los argumentos de CLI de Galaxy están en una lista de permitidos.
  • Las credenciales de repositorio se redactan en los logs y en el feed de eventos; = se escapa en los parámetros de configuración de Git.
  • Los endpoints de auditoría rechazan las lecturas entre sitios y los cursores mal formados; se validan las redes de proxy de confianza.
  • El unregister del runner se autentica; el cliente del runner rechaza las redirecciones.
  • Se verifican los ids de proyecto e integración de los webhooks.
  • Las tareas locales se detienen junto con todo su grupo de procesos.

Notas de actualización

Cambios incompatibles y de comportamiento

  1. Los nombres de plantilla deben ser únicos por proyecto. La migración v2.20.4 añade un índice único; renombra los duplicados antes de actualizar.
  2. Las tablas de roles se reconstruyen (v2.20.12 / v2.20.13). Haz una copia de seguridad primero. Un rol personalizado cuyo slug colisione sin distinguir mayúsculas con uno integrado o con otro rol se renombra (queda registrado); los permisos de plantilla que apuntan a un rol no resoluble se descartan (queda registrado). La pertenencia pasó a project__user_role; la columna project__user.role ha desaparecido.
  3. GET /api/project/{id}/users devuelve role solo para los miembros con un único rol — lee roles.
  4. /roles* pasó del indicador de administrador al permiso Gestionar roles globales; los administradores no se ven afectados.
  5. Los certificados LDAPS se verifican por defecto. Define ldap_tls_skip_verify o el tls_skip_verify de un proveedor para certificados autofirmados.
  6. ansible-playbook se invoca de forma distinta: --extra-vars @file, más un segundo archivo y un --vault-id <uuid>@prompt cuando la tarea tiene secretos, incluso para plantillas sin vault. Todo lo que analice la línea de comandos, o un ansible.cfg que prohíba las solicitudes de vault, queda afectado.
  7. Jinja2 dentro de los valores de las variables adicionales ya no se evalúa. Un playbook que dependía del templating de un valor de webhook o de encuesta dejará de hacerlo.
  8. El endpoint de ejecución de flujos de trabajo se renombró: POST …/workflows/{id}/run → …/runs.
  9. Los repositorios monorepo prevalecen sobre la plantilla: sin rama por plantilla, sin sobrescritura de rama ni de commit en las tareas.
  10. El repository_id del inventario se descarta para todos los tipos salvo file.
  11. Detener una tarea local mata su grupo de procesos (todavía sin equivalente en Windows).
  12. Las salidas de los flujos de trabajo aún no las capturan los ejecutores Docker y Kubernetes; los secretos de encuesta todavía no llegan a esos ejecutores.

Nueva configuración

audit, metrics, auth.max_session_life_hours, ssh.{config_path,known_hosts_file,strict_host_key_checking}, git_attempts, git_submodule_jobs, ldap_providers, ldap_tls_skip_verify, external_auth_email_matching, groups_claim y require_verified_email por proveedor OIDC, runner.check_interval_seconds, runner.connection.timeout_seconds, runners.{default_global_runners_mode,registration_token}. Todas opcionales. Los campos de configuración de tipo puntero pueden definirse ahora desde variables de entorno. Las páginas de referencia de la configuración y de la CLI se generan a partir del código fuente.

Migraciones de base de datos

v2.20.0 identidades externas · v2.20.1 secretos de encuesta ligados a la tarea · v2.20.2 nodos de retardo · v2.20.3 directorio de trabajo · v2.20.4 nombres de plantilla únicos · v2.20.5 silenciado de alertas de error · v2.20.7 configuración de hosts · v2.20.8 revisiones de flujo de trabajo · v2.20.9–v2.20.11 registro de auditoría e índices, asignaciones de entradas en las aristas, indicador monorepo · v2.20.12–v2.20.13 tablas de roles, variables de flujo de trabajo · v2.20.14 integraciones de flujo de trabajo y asignaciones de grupo · v2.20.16 roles globales.

Dependencias

Go 1.26; imágenes Docker sobre Alpine 3.24; se añade prometheus/client_golang 1.23; go-oidc 3.21, go-ldap 3.4.14, golang.org/x/crypto 0.57, modernc.org/sqlite 1.59, logrus 1.10; en el frontend js-yaml 5 y cron-parser 5.10. Nuevos despliegues con un clic: Cloudzy, RepoCloud y Pethost.