<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Asier Caballero</title>
    <description>The latest articles on DEV Community by Asier Caballero (@acrdev).</description>
    <link>https://dev.to/acrdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1438753%2F8023fe69-2654-4b75-a3af-d462ca89b026.png</url>
      <title>DEV Community: Asier Caballero</title>
      <link>https://dev.to/acrdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/acrdev"/>
    <language>en</language>
    <item>
      <title>Cómo montar un motor de contenidos que no te arruine (julio 2026)</title>
      <dc:creator>Asier Caballero</dc:creator>
      <pubDate>Tue, 28 Jul 2026 12:43:18 +0000</pubDate>
      <link>https://dev.to/acrdev/como-montar-un-motor-de-contenidos-que-no-te-arruine-julio-2026-1jel</link>
      <guid>https://dev.to/acrdev/como-montar-un-motor-de-contenidos-que-no-te-arruine-julio-2026-1jel</guid>
      <description>&lt;h1&gt;
  
  
  Cómo montar un motor de contenidos que no te arruine (julio 2026)
&lt;/h1&gt;

&lt;p&gt;Si sigues pagando 300 euros al mes por herramientas "todo en uno" de marketing, estás tirando el dinero. A mediados de 2026, la tecnología para automatizar ha bajado tanto de precio que los costes de infraestructura de contenidos son casi ridículos. La clave no es la herramienta cara, es conectar piezas pequeñas con APIs baratas.&lt;/p&gt;

&lt;p&gt;Aquí tienes cómo tengo montado mi flujo de trabajo ahora mismo.&lt;/p&gt;

&lt;h3&gt;
  
  
  La pila tecnológica (el stack)
&lt;/h3&gt;

&lt;p&gt;Para automatizar sin gastar, olvida las plataformas de marketing tipo HubSpot o plataformas cerradas. Mi setup actual es este:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Cerebro:&lt;/strong&gt; Claude 3.5 Sonnet (vía API). Es mejor razonando que GPT-4o para tono editorial.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Orquestador:&lt;/strong&gt; n8n (corriendo en una VPS de 5 euros al mes en Hetzner).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Base de datos:&lt;/strong&gt; Notion (vía API para gestionar el calendario).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Distribución:&lt;/strong&gt; Ghost para el blog y la API de LinkedIn/X para el alcance.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Coste total:&lt;/strong&gt; Menos de 15 euros al mes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Paso 1: El disparador (el calendario en Notion)
&lt;/h3&gt;

&lt;p&gt;No uses un Excel. Usa una base de datos de Notion con cuatro columnas: &lt;code&gt;Estado&lt;/code&gt;, &lt;code&gt;Título&lt;/code&gt;, &lt;code&gt;Prompt_Contexto&lt;/code&gt; y &lt;code&gt;Fecha_Publicación&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Cuando cambias el estado de "Borrador" a "Listo para generar", el webhook de n8n se dispara. Aquí es donde empieza el ahorro. No envías toda la base de datos, envías solo el registro nuevo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 2: El prompt como código, no como texto
&lt;/h3&gt;

&lt;p&gt;La mayoría de la gente comete el error de pedirle a la IA: "escribe un post sobre X". Sale basura genérica. En 2026, si no das contexto, el contenido no posiciona ni recibe interacción.&lt;/p&gt;

&lt;p&gt;En tu nodo de n8n, construye el prompt de forma dinámica. Así es como envío la estructura a la API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-3-5-sonnet-20260620"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"system"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Eres un redactor técnico senior especializado en SaaS B2B. Tu estilo es directo, sin paja, sin adjetivos innecesarios. Evitas los clichés de marketing de 2024. Tu objetivo es educar, no vender."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"messages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Escribe un artículo corto basado en este punto clave: {{ $json.titulo }}. Usa este contexto interno: {{ $json.contexto }}. Formato Markdown. Incluye 2 subtítulos h2."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La clave aquí es la variable &lt;code&gt;system&lt;/code&gt;. Define qué no quieres que diga. Si le pides que no sea "optimista" o que no use "listas de 5 pasos", obtendrás algo mucho más humano.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 3: El truco de la edición humana (no automatices el 100%)
&lt;/h3&gt;

&lt;p&gt;Automatizar todo el proceso hasta la publicación es un error. En julio de 2026, Google y LinkedIn castigan el contenido que se nota demasiado "sintético".&lt;/p&gt;

&lt;p&gt;Mi flujo hace esto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;n8n genera el borrador.&lt;/li&gt;
&lt;li&gt;n8n lo sube a Notion en una página nueva.&lt;/li&gt;
&lt;li&gt;Recibo una notificación en Slack: "Post generado, revisa".&lt;/li&gt;
&lt;li&gt;Entro, cambio dos párrafos, añado una experiencia personal (eso la IA no lo sabe) y le doy al botón de "Publicar".&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ese proceso me lleva 10 minutos. Escribir desde cero me llevaría dos horas. He ahorrado el 90% del tiempo manteniendo el control de calidad.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Por qué n8n y no Zapier?
&lt;/h3&gt;

&lt;p&gt;Es una cuestión de coste y control. Zapier te cobra por cada "paso" y por cada ejecución. Si tienes 10 posts al mes, Zapier te va a salir caro rápidamente. Con n8n en tu propia VPS, las ejecuciones son gratis. Instalarlo es tan sencillo como tirar un &lt;code&gt;docker-compose up&lt;/code&gt;. Si no sabes de servidores, pide a un colega que te lo instale en una hora. Es una inversión de una sola vez.&lt;/p&gt;

&lt;h3&gt;
  
  
  Datos, no opiniones
&lt;/h3&gt;

&lt;p&gt;Desde enero de 2026, he pasado de publicar 1 artículo a la semana a 4. Mi coste por contenido generado es de apenas 0,15 USD en tokens de API. &lt;/p&gt;

&lt;p&gt;¿Ha bajado la calidad? Al principio sí. Pero ajustando el &lt;em&gt;System Prompt&lt;/em&gt; y alimentando a la IA con mis propios artículos antiguos, he logrado que el tono sea prácticamente idéntico al mío. &lt;/p&gt;

&lt;p&gt;Un error que veo repetirse: gente que conecta la API de la IA directamente al blog sin revisar. Eso es suicidio para tu marca personal. Si el algoritmo de búsqueda de mediados de 2026 detecta patrón repetitivo, te mete en el &lt;em&gt;sandbox&lt;/em&gt; y no sales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo escalar esto sin morir en el intento
&lt;/h3&gt;

&lt;p&gt;Cuando ya tengas tu motor de contenidos rodando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Reutilización:&lt;/strong&gt; No crees contenido nuevo siempre. Crea un flujo que tome tu post de blog, lo resuma, y lo convierta en un hilo de X y un post de LinkedIn. Todo desde el mismo n8n.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Curación:&lt;/strong&gt; En lugar de pedirle a la IA que "invente" un artículo, pásale un PDF de un reporte de tu industria o un vídeo de YouTube (transcrito). Pídele que extraiga 3 lecciones clave. Eso es contenido de alto valor, no el relleno de siempre.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lo que viene (y cómo prepararse)
&lt;/h3&gt;

&lt;p&gt;A estas alturas del año, estamos viendo que los modelos empiezan a ser mejores integrando datos en tiempo real. Si tu blog no está enlazado a fuentes de datos vivas, vas a quedar obsoleto. &lt;/p&gt;

&lt;p&gt;Mi próxima fase de automatización: conectar la API de Google Search Console a mi n8n. Si un post deja de recibir tráfico, quiero que la IA me avise automáticamente y sugiera una actualización basándose en lo que están buscando los usuarios ese mes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resumen para el lunes por la mañana:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;No pagues suscripciones de marketing.&lt;/strong&gt; Monta n8n en una VPS.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Usa Claude 3.5 Sonnet.&lt;/strong&gt; Es el estándar actual para calidad técnica.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Mantén el humano en el bucle.&lt;/strong&gt; Que la IA redacte el 80%, tú pones el criterio y la experiencia real.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;No escales basura.&lt;/strong&gt; Si el contenido no aporta nada nuevo, no lo automatices, elimínalo de tu calendario.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si tienes dudas sobre cómo conectar alguna API específica, déjame un mensaje en el servidor de Discord o por LinkedIn. No te compliques, empieza por automatizar el calendario en Notion y ve subiendo la complejidad desde ahí. No hace falta más.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Marketing de contenidos en 2026: Automatización barata para gente sin equipo</title>
      <dc:creator>Asier Caballero</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:47:46 +0000</pubDate>
      <link>https://dev.to/acrdev/marketing-de-contenidos-en-2026-automatizacion-barata-para-gente-sin-equipo-2c01</link>
      <guid>https://dev.to/acrdev/marketing-de-contenidos-en-2026-automatizacion-barata-para-gente-sin-equipo-2c01</guid>
      <description>&lt;h1&gt;
  
  
  Marketing de contenidos en 2026: Automatización barata para gente sin equipo
&lt;/h1&gt;

&lt;p&gt;Si todavía escribes cada post a mano y lo programas en LinkedIn uno por uno, estás tirando el dinero y el tiempo. Estamos en julio de 2026. La IA ya no alucina tanto como hace año y medio y las APIs de los modelos locales son estables y casi gratis. &lt;/p&gt;

&lt;p&gt;No necesitas un equipo de marketing de cinco personas. Necesitas un par de scripts de Python, una cuenta de API barata y un sistema que trabaje mientras duermes. Aquí tienes cómo montar tu factoría de contenido por menos de 50 euros al mes.&lt;/p&gt;

&lt;h3&gt;
  
  
  La pila tecnológica (Stack 2026)
&lt;/h3&gt;

&lt;p&gt;Olvídate de contratar agencias que te cobran 2.000 pavos por "gestión de marca". Vamos a usar esto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Local LLM (Ollama con Llama 3.3 o DeepSeek V3):&lt;/strong&gt; Si tienes una GPU decente en casa, el coste es cero. Si no, alquila un nodo en RunPod por céntimos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n8n (autohospedado):&lt;/strong&gt; Es el pegamento. Conecta tu base de conocimientos con tus redes sociales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ghost o un WordPress headless:&lt;/strong&gt; Para el blog, no te compliques con constructores visuales pesados que cargan lento en móvil.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pinecone o Qdrant:&lt;/strong&gt; Para tu base de conocimientos (RAG).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Paso 1: Crea tu "Cerebro" (RAG)
&lt;/h3&gt;

&lt;p&gt;El error número uno es pedirle a la IA que escriba sobre "tendencias de negocios digitales". Te va a soltar basura genérica. &lt;/p&gt;

&lt;p&gt;Tienes que darle tus propios artículos, tus hilos de X (antes Twitter) y tus notas de Notion. Crea una base de datos vectorial. Cuando quieras escribir un post nuevo, el script busca primero qué has dicho tú sobre ese tema antes.&lt;/p&gt;

&lt;p&gt;Aquí tienes el concepto lógico en Python para extraer texto de tus notas y subirlo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.document_loaders&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NotionDirectoryLoader&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;langchain.vectorstores&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Qdrant&lt;/span&gt;

&lt;span class="c1"&gt;# Carga tus notas de negocios
&lt;/span&gt;&lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NotionDirectoryLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;./mis_notas_negocio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Sube a Qdrant (puedes usar la versión gratuita en la nube)
&lt;/span&gt;&lt;span class="n"&gt;vectorstore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Qdrant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;embedding_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-3-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:6333&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mi_conocimiento&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Paso 2: El motor de redacción (Automatización)
&lt;/h3&gt;

&lt;p&gt;No pongas un botón de "escribir post". Crea una carpeta en Google Drive o una columna en Notion llamada "Ideas". En cuanto cae una idea ahí, n8n debe disparar el flujo.&lt;/p&gt;

&lt;p&gt;El prompt que uso este mes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Actúa como un estratega de negocios digitales. Lee el contexto adjunto de mis notas. Escribe un post para LinkedIn/Blog basado en la idea [TÍTULO]. Tono: cínico, directo, sin relleno. Estructura: gancho fuerte, 3 puntos clave, conclusión con llamada a la acción. Usa formato Markdown."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Esto te ahorra horas de redacción. La IA escribe el borrador, tú lo revisas 5 minutos y lo lanzas. &lt;/p&gt;

&lt;h3&gt;
  
  
  Paso 3: Distribución multicanal
&lt;/h3&gt;

&lt;p&gt;El contenido no debe morir en el blog. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;El post se publica en tu web vía API (usando Ghost API).&lt;/li&gt;
&lt;li&gt;n8n detecta el nuevo post.&lt;/li&gt;
&lt;li&gt;Envía el contenido a tu LLM y le pides: "Transforma esto en un hilo de X de 5 tweets y un post corto para LinkedIn".&lt;/li&gt;
&lt;li&gt;Programa la publicación.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ahorro: unas 10-15 horas semanales. Coste: el consumo de tokens de la API de DeepSeek es ridículamente bajo en 2026. Por 5 euros al mes cubres el volumen de un creador medio.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Dónde está la trampa?
&lt;/h3&gt;

&lt;p&gt;La trampa es la edición. Si automatizas el 100% y no tocas el texto, se nota. Google y las plataformas sociales detectan los patrones de escritura de los LLMs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Regla de oro:&lt;/strong&gt; Usa la IA para la estructura y el 80% del texto. Usa tu tiempo para meter ejemplos reales, nombres de herramientas que usas tú y opiniones controvertidas. La IA no tiene enemigos, tú sí. Úsalos en tus textos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Un caso real: Julio 2026
&lt;/h3&gt;

&lt;p&gt;Tengo un cliente que vende servicios de automatización de CRMs. Antes, él escribía dos veces por semana. Ahora, tenemos un sistema que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Escanea las noticias de tecnología en RSS.&lt;/li&gt;
&lt;li&gt;Si algo relevante aparece, me manda un resumen a Telegram.&lt;/li&gt;
&lt;li&gt;Si le doy "ok" desde Telegram, el sistema escribe el artículo, lo maquetar en Ghost, lo publica y lo distribuye.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Resultado: Ha pasado de 2 posts al mes a 3 por semana. El tráfico orgánico ha subido un 40% en seis meses. No ha gastado un euro en publicidad de pago.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por qué ahora es el momento
&lt;/h3&gt;

&lt;p&gt;Hace dos años, configurar n8n y los vectores era una pesadilla técnica. Hoy, los templates de n8n para RAG vienen preconfigurados. Solo tienes que meter tus claves de API.&lt;/p&gt;

&lt;p&gt;Si todavía estás pagando a un redactor que cobra 100 euros por artículo para que te escriba cosas mediocres que no leen ni tus padres, despídelo. O mejor: dale acceso a estas herramientas y enséñale a editarlas. Si no lo haces tú, lo hará tu competencia antes de que acabe el año.&lt;/p&gt;

&lt;h3&gt;
  
  
  Checklist para montar esto este fin de semana:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Instala n8n en un servidor barato (Hetzner, 5€/mes).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crea una cuenta en DeepSeek o Groq (la API de Groq vuela).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vuelca todos tus textos pasados a una base de datos vectorial.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Configura el webhook de tu herramienta de notas (Notion o Obsidian).&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No intentes automatizar todo a la vez.&lt;/strong&gt; Empieza solo por la publicación en LinkedIn.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No busques la perfección. Busca el volumen y la consistencia. En 2026, si no estás presente en los feeds de tu nicho, no existes. Y para estar presente, necesitas automatizar. Sin más.&lt;/p&gt;

&lt;p&gt;¿Tienes dudas de cómo configurar la parte de la base vectorial con Obsidian? Deja de complicarte, usa un plugin de sincronización con la API de tu vector store y tira millas. No es ciencia espacial, es ingeniería de procesos aplicada a los negocios.&lt;/p&gt;

&lt;p&gt;Ahora, cierra el navegador y ponte a montar el flujo. El conocimiento no sirve de nada si no ejecutas.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>automation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deja de hacer despliegues manuales: CI/CD básico con GitHub Actions</title>
      <dc:creator>Asier Caballero</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:21:02 +0000</pubDate>
      <link>https://dev.to/acrdev/deja-de-hacer-despliegues-manuales-cicd-basico-con-github-actions-40j9</link>
      <guid>https://dev.to/acrdev/deja-de-hacer-despliegues-manuales-cicd-basico-con-github-actions-40j9</guid>
      <description>&lt;h1&gt;
  
  
  Deja de hacer despliegues manuales: CI/CD básico con GitHub Actions
&lt;/h1&gt;

&lt;p&gt;Si todavía copias archivos por SCP o subes carpetas por FTP en julio de 2026, por favor, para. No tiene sentido. GitHub Actions es el estándar de facto y si no lo usas, estás perdiendo el tiempo que podrías dedicar a cosas más interesantes.&lt;/p&gt;

&lt;p&gt;Vamos al grano. Un pipeline no es magia. Es solo un script que se ejecuta en una máquina virtual de GitHub cada vez que haces un &lt;code&gt;git push&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  El concepto clave: Eventos y Runners
&lt;/h3&gt;

&lt;p&gt;Todo se basa en eventos. Puedes disparar tu pipeline cuando haces un push a &lt;code&gt;main&lt;/code&gt;, cuando abres un Pull Request, o incluso a una hora específica.&lt;/p&gt;

&lt;p&gt;GitHub te presta una máquina (el "Runner"). Tú le dices qué hacer en un archivo YAML dentro de &lt;code&gt;.github/workflows/&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tu primer workflow
&lt;/h3&gt;

&lt;p&gt;Crea el archivo &lt;code&gt;.github/workflows/ci.yml&lt;/code&gt; en tu repositorio. Este es un ejemplo para una app sencilla de Node.js que deberías tener funcional hoy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI Pipeline&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;22'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Qué hace esto?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;on&lt;/code&gt;: Escucha cambios en &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;runs-on&lt;/code&gt;: Pide una máquina con Ubuntu actualizada a la fecha.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;actions/checkout&lt;/code&gt;: Descarga tu código en la máquina.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setup-node&lt;/code&gt;: Prepara el entorno.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt;: Ejecuta los comandos que escribirías en tu terminal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si el &lt;code&gt;npm test&lt;/code&gt; falla, el pipeline se detiene y te manda un correo. Así es como mantienes el código roto lejos de producción.&lt;/p&gt;

&lt;h3&gt;
  
  
  No pongas secretos en el repo
&lt;/h3&gt;

&lt;p&gt;Esto es un error de novato nivel junior: subir contraseñas o tokens de API al repo. GitHub tiene "Secrets" para esto.&lt;/p&gt;

&lt;p&gt;Ve a &lt;strong&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions&lt;/strong&gt;. Ahí guardas tu &lt;code&gt;DB_PASSWORD&lt;/code&gt; o tu &lt;code&gt;AWS_ACCESS_KEY&lt;/code&gt;. En el YAML, los llamas así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DB_PASSWORD }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nunca, bajo ninguna circunstancia, escribas el valor real en el archivo. Si lo haces, revócalo inmediatamente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Despliegue automático (CD)
&lt;/h3&gt;

&lt;p&gt;Aquí es donde ahorras horas. Una vez que los tests pasan, puedes desplegar automáticamente a tu nube de preferencia.&lt;/p&gt;

&lt;p&gt;Si usas Docker, el flujo lógico es:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build de la imagen.&lt;/li&gt;
&lt;li&gt;Login en tu registry (GHCR, AWS ECR, lo que sea).&lt;/li&gt;
&lt;li&gt;Push de la imagen.&lt;/li&gt;
&lt;li&gt;SSH al servidor o comando de actualización en Kubernetes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ejemplo para subir una imagen a GitHub Container Registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;build-and-push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login a GHCR&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build y Push&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}/app:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fíjate en el &lt;code&gt;needs: test&lt;/code&gt;. Si los tests fallan, la parte de despliegue ni siquiera arranca. Eso es seguridad.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lo que veo que falla siempre
&lt;/h3&gt;

&lt;p&gt;He revisado muchos repos de clientes a mediados de este 2026 y estos son los tres problemas más comunes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dependencias no cacheadas&lt;/strong&gt;: Si cada vez que corres un test bajas todo &lt;code&gt;node_modules&lt;/code&gt; o &lt;code&gt;pip&lt;/code&gt; desde cero, el pipeline tarda 5 minutos en vez de 30 segundos. Usa &lt;code&gt;actions/cache&lt;/code&gt; o, mejor aún, las capacidades integradas de &lt;code&gt;setup-node&lt;/code&gt; y &lt;code&gt;setup-python&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Workflows gigantes&lt;/strong&gt;: No intentes meter todo en un solo YAML de 500 líneas. Si tu pipeline crece, usa &lt;em&gt;Reusable Workflows&lt;/em&gt;. Si tienes un proceso de test que usas en 10 proyectos diferentes, sepáralo.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ignorar las matrices (Matrix Builds)&lt;/strong&gt;: Si tu app debe funcionar en Node 20, 22 y 24, no hagas tres pipelines. Usa la estrategia de matriz:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;20&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;22&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;24&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub ejecutará tres trabajos en paralelo. Es mucho más eficiente que hacerlo uno tras otro.&lt;/p&gt;

&lt;h3&gt;
  
  
  Un consejo final sobre seguridad
&lt;/h3&gt;

&lt;p&gt;En 2026, los ataques a cadenas de suministro están a la orden del día. Si vas a usar acciones de terceros (no creadas por GitHub o por la organización oficial de tu lenguaje), &lt;strong&gt;usa el hash del commit&lt;/strong&gt; en lugar de la versión (ej: &lt;code&gt;@v4&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Ejemplo:&lt;br&gt;
&lt;code&gt;uses: actions/checkout@b4ffde65f46336abfa881881184668721206f353&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Sí, es un poco más pesado de mantener, pero si alguien hackea el repositorio de esa acción y publica una versión maliciosa, tú estás protegido porque tu pipeline sigue apuntando al commit seguro.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Y ahora qué?
&lt;/h3&gt;

&lt;p&gt;No intentes automatizar todo el primer día. Empieza con el &lt;code&gt;npm test&lt;/code&gt; en el pipeline. Cuando eso esté sólido, añade el despliegue automático. Luego añade análisis estático de código (como SonarQube o ESLint) en el mismo flujo.&lt;/p&gt;

&lt;p&gt;El objetivo del DevOps no es llenar el repo de YAMLs, es que tú no tengas que estar pendiente de si el deploy salió bien o si el código que subió el compañero rompió la base de datos. Que la máquina trabaje por ti.&lt;/p&gt;

&lt;p&gt;Si tienes dudas, mira la documentación oficial. GitHub Actions ha cambiado mucho desde hace un par de años; ya es mucho más rápido y estable. Deja de pelearte con scripts de bash locales y centraliza esto en la plataforma donde vive tu código.&lt;/p&gt;

&lt;p&gt;¿Tienes algún problema con un error específico de &lt;code&gt;runner&lt;/code&gt;? Déjame un comentario y lo miramos.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programacion</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>CI/CD pipelines</title>
      <dc:creator>Asier Caballero</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:07:01 +0000</pubDate>
      <link>https://dev.to/acrdev/acrdev-article-l6c</link>
      <guid>https://dev.to/acrdev/acrdev-article-l6c</guid>
      <description>&lt;p&gt;Si estás harto de hacer &lt;code&gt;git push&lt;/code&gt; y luego entrar al servidor por SSH a hacer un &lt;code&gt;git pull&lt;/code&gt; y reiniciar manualmente el proceso, ya tienes la mitad del camino andado. Estás listo para dejar de ser un operario de tu propio código y empezar a delegar en GitHub Actions.&lt;/p&gt;

&lt;p&gt;A mediados de 2026, la realidad es que si tu pipeline no está automatizado, estás perdiendo tiempo valioso. GitHub Actions ha madurado un montón. Ya no es solo esa herramienta "curiosa" que probamos hace años; es el estándar de facto. Vamos a montar un flujo de trabajo serio sin marearte con teoría innecesaria.&lt;/p&gt;

&lt;h3&gt;
  
  
  La estructura del archivo .yaml
&lt;/h3&gt;

&lt;p&gt;Todo empieza en la carpeta &lt;code&gt;.github/workflows/&lt;/code&gt;. Ahí es donde viven tus automatizaciones. Cada archivo &lt;code&gt;.yaml&lt;/code&gt; en ese directorio es un &lt;em&gt;workflow&lt;/em&gt; independiente. Olvídate de herramientas externas; esto vive pegado a tu repo, lo que significa que el versionado de tu infraestructura de despliegue va de la mano con tu código.&lt;/p&gt;

&lt;p&gt;Para que esto funcione, necesitas entender el &lt;em&gt;trigger&lt;/em&gt; (disparador). ¿Cuándo quieres que corra esto? Normalmente, en cada &lt;code&gt;push&lt;/code&gt; a la rama &lt;code&gt;main&lt;/code&gt; o en cada &lt;code&gt;pull request&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI Pipeline&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;22'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install and Test&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm ci&lt;/span&gt;
          &lt;span class="s"&gt;npm run test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aquí hay un detalle técnico clave: &lt;code&gt;npm ci&lt;/code&gt; en lugar de &lt;code&gt;npm install&lt;/code&gt;. En un entorno de CI, no quieres actualizar dependencias por accidente. &lt;code&gt;npm ci&lt;/code&gt; asegura que vas a instalar exactamente lo que dice tu &lt;code&gt;package-lock.json&lt;/code&gt;. Si el lock no coincide, el proceso falla. Eso es lo que queremos: consistencia.&lt;/p&gt;

&lt;h3&gt;
  
  
  La regla de oro: nunca subas secretos
&lt;/h3&gt;

&lt;p&gt;Uno de los errores más comunes cuando estás empezando con este tutorial de GitHub Actions es hardcodear credenciales en el archivo &lt;code&gt;.yaml&lt;/code&gt;. Por favor, no lo hagas. GitHub tiene una pestaña llamada "Secrets and variables" en la configuración de tu repositorio.&lt;/p&gt;

&lt;p&gt;Ahí es donde metes tu &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;, tu token de Docker Hub o la API Key de tu proveedor de Cloud. Luego, en tu archivo de workflow, los invocas así:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to Cloud&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;MY_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MY_API_KEY }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./deploy.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El log de GitHub Actions es inteligente y censura los valores de los secretos (los cambia por asteriscos), pero si los imprimes explícitamente en un &lt;code&gt;echo&lt;/code&gt;, podrían aparecer. Ten cuidado con los logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  El salto de CI a CD: Entornos y Despliegue
&lt;/h3&gt;

&lt;p&gt;La integración continua (CI) es fácil: es solo pasar tests. La entrega continua (CD) da más respeto. Mi consejo: empieza por automatizar el despliegue a un entorno de &lt;em&gt;Staging&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Usa los &lt;strong&gt;Environments&lt;/strong&gt; de GitHub. Puedes configurarlos para que requieran aprobación manual. Esto es oro puro para evitar que un bug llegue a producción por error. Vas a &lt;code&gt;Settings &amp;gt; Environments&lt;/code&gt; y creas uno llamado "production". Activa la casilla de "Required reviewers".&lt;/p&gt;

&lt;p&gt;Ahora, cuando tu pipeline llegue al job de despliegue, GitHub pausará la ejecución hasta que tú (o el lead técnico) hagas clic en "Approve". Esto te da una red de seguridad brutal.&lt;/p&gt;

&lt;p&gt;Para el despliegue en sí, en 2026 lo normal es mover contenedores. Si usas Docker, el flujo debería ser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build de la imagen.&lt;/li&gt;
&lt;li&gt;Login en el registry (GitHub Packages o ECR).&lt;/li&gt;
&lt;li&gt;Push con el tag basado en el SHA del commit (&lt;code&gt;${{ github.sha }}&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Actualización del deployment en Kubernetes o el servicio de contenedores que uses.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Performance: Por qué tus builds son lentos
&lt;/h3&gt;

&lt;p&gt;Si tu pipeline tarda 15 minutos en terminar, vas a terminar odiando hacer commits. Aquí es donde los principiantes fallan. Tienes que usar &lt;strong&gt;caching&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;GitHub Actions tiene una acción oficial para manejar caché. Si estás usando una app de Next.js o un proyecto de Go, no quieres descargar las dependencias de internet cada vez.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache node modules&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.npm&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-node-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Al incluir el &lt;code&gt;hashFiles('**/package-lock.json')&lt;/code&gt;, el caché solo se invalida cuando cambias tus dependencias. Si solo cambias código fuente, el job de &lt;code&gt;install&lt;/code&gt; será casi instantáneo. Esos segundos cuentan cuando tienes un equipo haciendo merge constantemente.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa cuando algo falla?
&lt;/h3&gt;

&lt;p&gt;Es inevitable. Un test fallará, el linter se quejará o el despliegue al cloud dará timeout. GitHub Actions te manda un email, pero es molesto. &lt;/p&gt;

&lt;p&gt;Lo mejor que puedes hacer es configurar notificaciones en Slack o Discord. Hay acciones prefabricadas, como &lt;code&gt;8398a7/action-slack&lt;/code&gt;, que te avisan con el enlace directo al commit que rompió la build. No esperes a que el cliente te diga que la web está caída; entérate tú primero.&lt;/p&gt;

&lt;h3&gt;
  
  
  Un último consejo de "colega"
&lt;/h3&gt;

&lt;p&gt;No intentes hacer todo el flujo de CI/CD perfecto el primer día. Empieza solo con los tests. Cuando confíes en que tu pipeline te avisa si rompes algo, añade el despliegue automático.&lt;/p&gt;

&lt;p&gt;Muchos se obsesionan con el "GitOps" avanzado desde el día uno y terminan con un archivo &lt;code&gt;.yaml&lt;/code&gt; de 400 líneas que nadie entiende. Mantén tus workflows modulares. Si una tarea es compleja, crea un script en bash (&lt;code&gt;scripts/deploy.sh&lt;/code&gt;) y llama al script desde el workflow. Así puedes probar el script en tu máquina local sin tener que hacer push a GitHub mil veces para ver si el comando está bien escrito.&lt;/p&gt;

&lt;p&gt;GitHub Actions es una herramienta potente, pero al final del día es código. Trátalo como tal: testeable, versionable y, sobre todo, sencillo. Si puedes explicar lo que hace tu workflow en una frase, vas por buen camino. Si necesitas un diagrama de flujo de diez páginas para entender cómo se despliega tu app, es hora de simplificar. &lt;/p&gt;

&lt;p&gt;¿Ya tienes tu primer &lt;code&gt;.yaml&lt;/code&gt; configurado? Si no, hoy es el mejor día para subir ese archivo al repo y dejar que GitHub haga el trabajo sucio por ti.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>automation</category>
      <category>tech</category>
    </item>
    <item>
      <title>Cancela ejecuciones anteriores si lanzas un nuevo commit en la misma PR</title>
      <dc:creator>Asier Caballero</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:03:28 +0000</pubDate>
      <link>https://dev.to/acrdev/cancela-ejecuciones-anteriores-si-lanzas-un-nuevo-commit-en-la-misma-pr-9fe</link>
      <guid>https://dev.to/acrdev/cancela-ejecuciones-anteriores-si-lanzas-un-nuevo-commit-en-la-misma-pr-9fe</guid>
      <description>&lt;p&gt;Todos hemos vivido ese sudor frío: haces &lt;code&gt;git push&lt;/code&gt; a &lt;code&gt;main&lt;/code&gt; un viernes a las seis de la tarde, cruzas los dedos y rezas para que el servidor de producción no empiece a escupir errores 500. Las oraciones no sustituyen a un pipeline de CI/CD bien configurado.&lt;/p&gt;

&lt;p&gt;Si sigues conectándote por SSH a una VPS para hacer un &lt;code&gt;git pull&lt;/code&gt; manual, o ejecutando la suite de tests en tu portátil antes de mergear "a ojo", necesitas automatizar esto ya. GitHub Actions se ha convertido en el estándar de facto para el nicho DevOps no solo porque está integrado donde vive tu código, sino porque la infraestructura que te da de gratis (o casi gratis) para runners es ridículamente buena.&lt;/p&gt;

&lt;p&gt;En este &lt;strong&gt;GitHub Actions tutorial&lt;/strong&gt; vamos a construir un pipeline completo, moderno y listo para producción, explicando qué pasa bajo el capó sin saltarnos los detalles técnicos que importan.&lt;/p&gt;




&lt;h3&gt;
  
  
  La anatomía de un Workflow (sin metáforas absurdas)
&lt;/h3&gt;

&lt;p&gt;Para trabajar con GitHub Actions solo necesitas entender cómo piensa el motor de GitHub. Todo se define en archivos YAML dentro del directorio &lt;code&gt;.github/workflows/&lt;/code&gt; de tu repositorio.&lt;/p&gt;

&lt;p&gt;El modelo mental se reduce a esto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Event (&lt;code&gt;on&lt;/code&gt;):&lt;/strong&gt; El detonante. Un &lt;code&gt;push&lt;/code&gt;, una &lt;code&gt;pull_request&lt;/code&gt;, un cron o un evento manual (&lt;code&gt;workflow_dispatch&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job:&lt;/strong&gt; Una secuencia de pasos que se ejecuta en una máquina virtual dedicada (Runner). Los jobs corren en paralelo por defecto, a menos que definas dependencias entre ellos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step:&lt;/strong&gt; Las tareas individuales dentro de un job. Un paso puede ejecutar un comando de terminal (&lt;code&gt;run&lt;/code&gt;) o reutilizar un bloque de código empaquetado (&lt;code&gt;uses&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action:&lt;/strong&gt; Ese bloque reutilizable de código (normalmente un repositorio público) que te ahorra escribir 50 líneas de Bash para configurar Node, instalar Docker o autenticarte en AWS.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Un Pipeline Real: De Linting a despliegue automatizado
&lt;/h3&gt;

&lt;p&gt;Vamos a montar un caso de uso real. Imagina una aplicación en Node.js (digamos v24) que queremos validar en cada Pull Request y desplegar como imagen Docker solo cuando el código llegue a la rama &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Crea el archivo &lt;code&gt;.github/workflows/ci-cd.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI/CD Pipeline&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Cancela ejecuciones anteriores si lanzas un nuevo commit en la misma PR&lt;/span&gt;
&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;quality-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Quality &amp;amp; Tests&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout del código&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configurar Node.js&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;24&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Instalar dependencias&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ejecutar Linter&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Correr tests unitarios&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;

  &lt;span class="na"&gt;build-and-push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build &amp;amp; Push Docker Image&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;quality-check&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name == 'push' &amp;amp;&amp;amp; github.ref == 'refs/heads/main'&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout del código&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Docker Buildx&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/setup-buildx-action@v3&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login en GitHub Container Registry&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/login-action@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.actor }}&lt;/span&gt;
          &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Extraer metadatos para Docker&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/metadata-action@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;images&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;type=raw,value=latest&lt;/span&gt;
            &lt;span class="s"&gt;type=sha,format=short&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build y Push de la imagen&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker/build-push-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.&lt;/span&gt;
          &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
          &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.tags }}&lt;/span&gt;
          &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ steps.meta.outputs.labels }}&lt;/span&gt;
          &lt;span class="na"&gt;cache-from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha&lt;/span&gt;
          &lt;span class="na"&gt;cache-to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;type=gha,mode=max&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Desgranando los detalles técnicos que marcan la diferencia
&lt;/h3&gt;

&lt;p&gt;El código anterior no es el típico ejemplo trivial de "Hello World". Tiene decisiones de arquitectura que deberías aplicar desde el día uno:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Control de concurrencia
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;concurrency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.workflow }}-${{ github.ref }}&lt;/span&gt;
  &lt;span class="na"&gt;cancel-in-progress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si haces tres pushes seguidos a una Pull Request en menos de un minuto, no quieres consumir minutos de runners ejecutando tres veces la suite de tests. Esta directiva mata automáticamente los workflows anteriores del mismo branch y deja activo solo el último.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Principio de mínimo privilegio
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por defecto, el token implícito que genera GitHub (&lt;code&gt;secrets.GITHUB_TOKEN&lt;/code&gt;) tenía históricamente demasiados permisos. Limitar explícitamente los ámbitos a nivel de workflow evita desastres de seguridad en caso de que una dependencia maliciosa intente inyectar código durante el build.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Caching agresivo de dependencias y Docker
&lt;/h4&gt;

&lt;p&gt;Fíjate en esto:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;En Node usas &lt;code&gt;cache: 'npm'&lt;/code&gt; dentro de &lt;code&gt;actions/setup-node&lt;/code&gt;. Esto guarda la carpeta &lt;code&gt;~/.npm&lt;/code&gt; entre ejecuciones usando el hash de tu &lt;code&gt;package-lock.json&lt;/code&gt; como clave.&lt;/li&gt;
&lt;li&gt;En la acción de Docker usas &lt;code&gt;cache-from: type=gha&lt;/code&gt; y &lt;code&gt;cache-to: type=gha,mode=max&lt;/code&gt;. Esto utiliza el sistema de cache nativo de GitHub Actions para las capas de Docker. La primera vez que compilas tu imagen tarda 2 minutos; las siguientes tardan 15 segundos porque aprovecha las capas sin cambios.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Encadenamiento de Jobs mediante &lt;code&gt;needs&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;El job &lt;code&gt;build-and-push&lt;/code&gt; tiene la directiva &lt;code&gt;needs: quality-check&lt;/code&gt;. Esto le dice al motor de GitHub: &lt;em&gt;"Ni se te ocurra empaquetar la imagen Docker si los tests fallaron en el job anterior"&lt;/em&gt;. Además, mediante la condición &lt;code&gt;if&lt;/code&gt;, nos aseguramos de que la imagen solo se publique cuando el evento sea un &lt;code&gt;push&lt;/code&gt; a la rama &lt;code&gt;main&lt;/code&gt;, ignorando las ejecuciones en PRs.&lt;/p&gt;




&lt;h3&gt;
  
  
  Secretos y Variables de Entorno: No metas la pata
&lt;/h3&gt;

&lt;p&gt;Uno de los errores de novato más comunes es dejar credenciales expuestas en la configuración o subir archivos &lt;code&gt;.env&lt;/code&gt; al repositorio.&lt;/p&gt;

&lt;p&gt;Para manejar variables sensibles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ve a tu repositorio en GitHub -&amp;gt; &lt;strong&gt;Settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Secrets and variables&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Actions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Define tus secretos ahí (por ejemplo, &lt;code&gt;AWS_ACCESS_KEY_ID&lt;/code&gt;, &lt;code&gt;DATABASE_URL&lt;/code&gt;, etc.).&lt;/li&gt;
&lt;li&gt;Consúmelos en tu YAML mediante el contexto de secretos:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Desplegar a infraestructura&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;DB_URI&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DATABASE_URL }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;./deploy-script.sh&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub enmascara automáticamente el valor de estos secretos en los logs de salida. Si un comando imprime por error un secreto, verás &lt;code&gt;***&lt;/code&gt; en la consola.&lt;/p&gt;




&lt;h3&gt;
  
  
  Cómo depurar cuando todo rompe (porque va a romper)
&lt;/h3&gt;

&lt;p&gt;El ciclo de desarrollo de un archivo YAML puede ser frustrante: &lt;em&gt;commit -&amp;gt; push -&amp;gt; esperar al runner -&amp;gt; error de sintaxis -&amp;gt; repetir&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Para evitar saturar tu historial de Git con commits del tipo "fix typo in yaml", tienes dos opciones excelentes:&lt;/p&gt;

&lt;h4&gt;
  
  
  Usar &lt;code&gt;act&lt;/code&gt; para probar en local
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/nektos/act" rel="noopener noreferrer"&gt;act&lt;/a&gt; es una herramienta en CLI que lee tu directorio &lt;code&gt;.github/workflows/&lt;/code&gt; y ejecuta los jobs localmente dentro de contenedores Docker.&lt;br&gt;
Simplemente ejecutas en tu terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;act pull_request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y verás cómo se procesa tu pipeline exacta en tu máquina local sin haber subido nada a GitHub.&lt;/p&gt;

&lt;h4&gt;
  
  
  Habilitar logs de depuración
&lt;/h4&gt;

&lt;p&gt;Si el problema ocurre directamente en los runners de GitHub, puedes crear un secreto en el repositorio llamado &lt;code&gt;ACTIONS_STEP_DEBUG&lt;/code&gt; con el valor &lt;code&gt;true&lt;/code&gt;. La próxima vez que corra el workflow, GitHub Actions escupirá un nivel de detalle extremo sobre qué variables de entorno se están exportando y qué comandos exactos está procesando la Shell bajo la superficie.&lt;/p&gt;




&lt;h3&gt;
  
  
  Siguientes pasos
&lt;/h3&gt;

&lt;p&gt;Con esta base ya tienes un flujo que previene que rompas producción y elimina el trabajo manual de empaquetar artefactos. &lt;/p&gt;

&lt;p&gt;A partir de aquí, el camino natural en DevOps pasa por explorar la estrategia de matrices (&lt;code&gt;strategy: matrix&lt;/code&gt;) para probar tu código en múltiples versiones del entorno de ejecución simultáneamente, o implementar &lt;strong&gt;environments&lt;/strong&gt; con aprobación manual antes de pasar de staging a producción. Pero no te compliques al principio: valida tu código, corre tus tests, construye tu imagen y automatiza el push. La magia del CI/CD es que la máquina trabaje para ti.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>automation</category>
      <category>tech</category>
    </item>
    <item>
      <title>Aquí tienes una guía completa, optimizada para SEO y estructurada técnicamente para el nicho DevOps.</title>
      <dc:creator>Asier Caballero</dc:creator>
      <pubDate>Tue, 28 Jul 2026 08:00:06 +0000</pubDate>
      <link>https://dev.to/acrdev/aqui-tienes-una-guia-completa-optimizada-para-seo-y-estructurada-tecnicamente-para-el-nicho-devops-579b</link>
      <guid>https://dev.to/acrdev/aqui-tienes-una-guia-completa-optimizada-para-seo-y-estructurada-tecnicamente-para-el-nicho-devops-579b</guid>
      <description>&lt;p&gt;Aquí tienes una guía completa, optimizada para SEO y estructurada técnicamente para el nicho DevOps.&lt;/p&gt;




&lt;h1&gt;
  
  
  Metadata SEO
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Título SEO:&lt;/strong&gt; GitHub Actions tutorial: Guía definitiva de CI/CD para principiantes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meta Description:&lt;/strong&gt; Aprende CI/CD desde cero con este GitHub Actions tutorial. Automatiza tu código con ejemplos prácticos, workflows y buenas prácticas DevOps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slug:&lt;/strong&gt; &lt;code&gt;github-actions-tutorial-cicd-principiantes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tags:&lt;/strong&gt; DevOps, GitHub Actions, CI/CD, Integración Continua, Automatización, YAML, Git&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  GitHub Actions tutorial: Guía definitiva de CI/CD para principiantes
&lt;/h1&gt;

&lt;p&gt;En el desarrollo de software moderno, la velocidad y la calidad no pueden ser mutuamente excluyentes. Desplegar código de forma manual, ejecutar pruebas localmente antes de cada &lt;em&gt;merge&lt;/em&gt; o configurar servidores a mano son prácticas del pasado que consumen tiempo y generan errores humanos. Aquí es donde entra en juego la automatización mediante &lt;strong&gt;CI/CD (Integración Continua y Despliegue Continuo)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Aunque existen múltiples herramientas en el mercado como Jenkins, GitLab CI o CircleCI, GitHub ha revolucionado el ecosistema integrando su propia solución nativa: &lt;strong&gt;GitHub Actions&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Si buscas automatizar tus flujos de trabajo sin salir de tu repositorio, has llegado al lugar correcto. En este &lt;strong&gt;GitHub Actions tutorial&lt;/strong&gt;, aprenderás desde los conceptos fundamentales hasta la creación de &lt;em&gt;pipelines&lt;/em&gt; profesionales listos para producción, paso a paso y con ejemplos de código reales.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. ¿Qué es GitHub Actions y por qué deberías usarlo?
&lt;/h2&gt;

&lt;p&gt;GitHub Actions es una plataforma de automatización de flujos de trabajo (&lt;em&gt;workflows&lt;/em&gt;) integrada directamente en la ecosistema de GitHub. Te permite ejecutar tareas automatizadas en respuesta a eventos específicos que ocurren en tu repositorio, como un &lt;code&gt;push&lt;/code&gt;, una &lt;code&gt;pull request&lt;/code&gt; o la creación de un &lt;code&gt;release&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  La importancia de CI/CD en el desarrollo moderno
&lt;/h3&gt;

&lt;p&gt;La &lt;strong&gt;Integración Continua (CI)&lt;/strong&gt; consiste en automatizar la compilación y pruebas del código cada vez que un desarrollador sube cambios. Por otro lado, el &lt;strong&gt;Despliegue Continuo (CD)&lt;/strong&gt; automatiza la entrega de ese código a entornos de staging o producción. Adopta estas prácticas para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reducir el tiempo de ciclo de desarrollo (&lt;em&gt;Time-to-Market&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Detectar errores (&lt;em&gt;bugs&lt;/em&gt;) en fases tempranas.&lt;/li&gt;
&lt;li&gt;Mantener la rama principal (&lt;code&gt;main&lt;/code&gt;/&lt;code&gt;master&lt;/code&gt;) siempre estable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Si quieres profundizar más sobre la filosofía tras estas prácticas, te recomendamos leer nuestro artículo sobre &lt;a href="https://tusitio.com/principios-fundamentales-devops" rel="noopener noreferrer"&gt;principios fundamentales de la metodología DevOps&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ventajas clave de GitHub Actions frente a otras herramientas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integración nativa:&lt;/strong&gt; No requiere instalar servidores externos ni configurar Webhooks complejos como ocurre con Jenkins.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecosistema masivo:&lt;/strong&gt; Cuenta con el &lt;em&gt;GitHub Marketplace&lt;/em&gt;, una biblioteca con miles de acciones prefabricadas por la comunidad y grandes empresas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matriz de entornos:&lt;/strong&gt; Soporte integrado para Linux, macOS y Windows en máquinas virtuales alojadas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generosa capa gratuita:&lt;/strong&gt; Ofrece minutos gratuitos mensuales tanto para repositorios públicos (ilimitados) como privados.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Conceptos clave que debes dominar en GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Antes de escribir la primera línea de código, es crucial entender la anatomía interna de GitHub Actions. La arquitectura se divide en varios componentes interconectados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Evento ] ---&amp;gt; ( Workflow ) ---&amp;gt; [ Job 1 ] ---&amp;gt; Step 1 -&amp;gt; Step 2
                                 ---&amp;gt; [ Job 2 ] ---&amp;gt; Step 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Workflows, Events, Jobs y Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Workflow (Flujo de trabajo):&lt;/strong&gt; Es un proceso automatizado configurable compuesto por uno o más &lt;em&gt;jobs&lt;/em&gt;. Se define mediante un archivo con extensión &lt;code&gt;.yml&lt;/code&gt; o &lt;code&gt;.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Event (Evento):&lt;/strong&gt; Es la actividad específica que desencadena la ejecución del &lt;em&gt;workflow&lt;/em&gt;. Ejemplos: &lt;code&gt;push&lt;/code&gt;, &lt;code&gt;pull_request&lt;/code&gt;, &lt;code&gt;schedule&lt;/code&gt; (cron jobs), o un evento manual (&lt;code&gt;workflow_dispatch&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job (Trabajo):&lt;/strong&gt; Un conjunto de pasos (&lt;em&gt;steps&lt;/em&gt;) que se ejecutan en un mismo ejecutor (&lt;em&gt;runner&lt;/em&gt;). Por defecto, si defines varios &lt;em&gt;jobs&lt;/em&gt;, estos se ejecutarán en paralelo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step (Paso):&lt;/strong&gt; Una tarea individual dentro de un &lt;em&gt;job&lt;/em&gt;. Puede ser la ejecución de un comando de terminal (&lt;code&gt;run&lt;/code&gt;) o el uso de una acción reutilizable (&lt;code&gt;uses&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action (Acción):&lt;/strong&gt; El bloque de construcción más pequeño. Son comandos individuales combinados en &lt;em&gt;steps&lt;/em&gt; para crear un &lt;em&gt;job&lt;/em&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Runners: ¿Servidores alojados o propios?
&lt;/h3&gt;

&lt;p&gt;Un &lt;strong&gt;Runner&lt;/strong&gt; es la máquina virtual o contenedor que ejecuta el &lt;em&gt;workflow&lt;/em&gt;. GitHub ofrece dos modalidades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub-hosted runners:&lt;/strong&gt; Máquinas gestionadas totalmente por GitHub (Ubuntu, Windows Server, macOS). Se limpian automáticamente tras cada ejecución.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosted runners:&lt;/strong&gt; Servidores propios (físicos, VMs o clusters de Kubernetes) que conectas a GitHub. Son ideales si necesitas hardware específico, conexiones a redes privadas o tiempos de ejecución personalizados. Para aprender a gestionar infraestructuras complejas, consulta nuestra guía sobre &lt;a href="https://tusitio.com/introduccion-terraform-iac" rel="noopener noreferrer"&gt;infraestructura como código con Terraform&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Tu primer Workflow: Hola Mundo en GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Manos a la obra. Crearás un &lt;em&gt;workflow&lt;/em&gt; básico que imprima un mensaje en la consola cada vez que hagas un &lt;em&gt;push&lt;/em&gt; a tu repositorio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Estructura del directorio &lt;code&gt;.github/workflows&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;GitHub Actions requiere que todos los archivos de configuración se almacenen en una ruta específica dentro de la raíz de tu proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mi-proyecto/
├── .github/
│   └── workflows/
│       └── hola-mundo.yml
├── src/
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Análisis sintáctico de un archivo YAML básico
&lt;/h3&gt;

&lt;p&gt;Crea el archivo &lt;code&gt;.github/workflows/hola-mundo.yml&lt;/code&gt; y añade el siguiente contenido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Primer Workflow - Hola Mundo&lt;/span&gt;

&lt;span class="c1"&gt;# Define el evento que dispara el workflow&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="c1"&gt;# Define los trabajos a ejecutar&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;saludo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt; &lt;span class="c1"&gt;# Entorno de ejecución&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Obtener el código del repositorio&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Imprimir mensaje en la consola&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "¡Hola Mundo desde GitHub Actions!"&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ejecutar un comando multilínea&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Este es un comando en la línea 1"&lt;/span&gt;
          &lt;span class="s"&gt;echo "El flujo se ejecutó correctamente"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Guarda el archivo, haz un &lt;code&gt;git commit&lt;/code&gt; y súbelo a GitHub. Ve a la pestaña &lt;strong&gt;Actions&lt;/strong&gt; en tu repositorio web para ver la ejecución en tiempo real.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Sintaxis YAML explicada paso a paso
&lt;/h2&gt;

&lt;p&gt;El formato YAML es sensible a la sangría (espaciado). Comprender cómo declarar los bloques te evitará horas de depuración.&lt;/p&gt;

&lt;h3&gt;
  
  
  Eventos de disparo (&lt;code&gt;on: push&lt;/code&gt;, &lt;code&gt;pull_request&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Puedes afinar cuándo debe ejecutarse un &lt;em&gt;pipeline&lt;/em&gt; usando filtros detallados:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;feature/**'&lt;/span&gt; &lt;span class="c1"&gt;# Se dispara en ramas como feature/login, feature/cart&lt;/span&gt;
    &lt;span class="na"&gt;paths-ignore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**.md'&lt;/span&gt;      &lt;span class="c1"&gt;# Igora el pipeline si solo se modificaron archivos Markdown&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;reopened&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Uso de acciones del Marketplace (&lt;code&gt;actions/checkout&lt;/code&gt;, &lt;code&gt;actions/setup-node&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;En lugar de escribir scripts desde cero para configurar lenguajes o herramientas, reutilizamos &lt;em&gt;actions&lt;/em&gt; del Marketplace.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;actions/checkout@v4&lt;/code&gt;: Clona tu código dentro del &lt;em&gt;runner&lt;/em&gt; para que el &lt;em&gt;job&lt;/em&gt; pueda acceder a él.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;actions/setup-node@v4&lt;/code&gt;: Configura un entorno de Node.js especificando la versión requerida.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Descargar Código&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configurar Node.js&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Implementando Integración Continua (CI) paso a paso
&lt;/h2&gt;

&lt;p&gt;Llevemos esto a un nivel profesional. Vamos a construir un &lt;em&gt;pipeline&lt;/em&gt; de Integración Continua completo para un proyecto en Node.js que valida el código, ejecuta pruebas unitarias y comprueba la calidad de sintaxis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuración de pruebas automáticas y linter
&lt;/h3&gt;

&lt;p&gt;Crea un archivo &lt;code&gt;.github/workflows/ci-pipeline.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pipeline de Integración Continua (CI)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout del proyecto&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configurar Node.js 20.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm'&lt;/span&gt; &lt;span class="c1"&gt;# Habilita el almacenamiento en caché de dependencias&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Instalar dependencias&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verificar Estilo (Linter)&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run lint --if-present&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ejecutar Pruebas Unitarias&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este &lt;em&gt;job&lt;/em&gt; bloqueará la fusión (&lt;em&gt;merge&lt;/em&gt;) de cualquier &lt;em&gt;Pull Request&lt;/em&gt; si las pruebas unitarias o el &lt;em&gt;linter&lt;/em&gt; fallan, garantizando la calidad del software.&lt;/p&gt;

&lt;h3&gt;
  
  
  Manejo de matrices de ejecución (Matrix Strategy)
&lt;/h3&gt;

&lt;p&gt;¿Qué ocurre si necesitas asegurar que tu aplicación funciona en múltiples versiones de Node.js o en diferentes sistemas operativos? Usamos una &lt;strong&gt;Matrix Strategy&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test-matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.os }}&lt;/span&gt;
    &lt;span class="na"&gt;strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;matrix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;os&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;windows-latest&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;18.x&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;20.x&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Usar Node.js ${{ matrix.node-version }}&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ matrix.node-version }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este bloque ejecutará &lt;strong&gt;4 trabajos en paralelo&lt;/strong&gt; ($2\text{ SO} \times 2\text{ versiones de Node}$).&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Gestión de Secretos y Variables de Entorno
&lt;/h2&gt;

&lt;p&gt;Nunca, bajo ninguna circunstancia, debes subir claves API, tokens de bases de datos o contraseñas en código plano a tu repositorio.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo almacenar credenciales de forma segura
&lt;/h3&gt;

&lt;p&gt;GitHub provee un almacén cifrado seguro:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;En tu repositorio, ve a &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;Secrets and variables&lt;/strong&gt; &amp;gt; &lt;strong&gt;Actions&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Haz clic en &lt;strong&gt;New repository secret&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Asigna un nombre en mayúsculas (ej. &lt;code&gt;HEROKU_API_KEY&lt;/code&gt; o &lt;code&gt;AWS_SECRET_ACCESS_KEY&lt;/code&gt;) y pega el valor confidencial.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Inyección de variables en tu Pipeline
&lt;/h3&gt;

&lt;p&gt;Puedes acceder a estos datos dentro de tu YAML utilizando el contexto &lt;code&gt;secrets&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Conectar a Servicio Externo&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.MY_API_SECRET }}&lt;/span&gt;
      &lt;span class="na"&gt;DB_HOST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.PUBLIC_DB_HOST }}&lt;/span&gt; &lt;span class="c1"&gt;# Variable no confidencial&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;python run_migration.py&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para complementar la seguridad de tu infraestructura, te sugerimos leer nuestro artículo sobre &lt;a href="https://tusitio.com/gestion-de-secretos-devops" rel="noopener noreferrer"&gt;estrategias de gestión de secretos en entornos Cloud&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Despliegue Continuo (CD): Llevando tu aplicación a Producción
&lt;/h2&gt;

&lt;p&gt;Una vez que el código ha pasado todas las pruebas de CI, es momento de desplegarlo. &lt;/p&gt;

&lt;h3&gt;
  
  
  Automatizando el despliegue en un servidor o servicio Cloud
&lt;/h3&gt;

&lt;p&gt;A continuación, vemos un ejemplo de despliegue automatizado hacia un servidor remoto mediante SSH utilizando Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Despliegue Continuo (CD)&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout del código&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Desplegar en Servidor VPS vía SSH&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;appleboy/ssh-action@v1.0.0&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_HOST }}&lt;/span&gt;
          &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SERVER_USER }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SSH_PRIVATE_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;cd /var/www/mi-app&lt;/span&gt;
            &lt;span class="s"&gt;git pull origin main&lt;/span&gt;
            &lt;span class="s"&gt;docker-compose down&lt;/span&gt;
            &lt;span class="s"&gt;docker-compose up -d --build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si tu arquitectura utiliza contenedores, revisa nuestra guía sobre &lt;a href="https://tusitio.com/optimizar-imagenes-docker" rel="noopener noreferrer"&gt;cómo optimizar imágenes de Docker para producción&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entornos de despliegue y aprobaciones manuales
&lt;/h3&gt;

&lt;p&gt;GitHub permite definir &lt;strong&gt;Environments&lt;/strong&gt; (ej. &lt;em&gt;Staging&lt;/em&gt;, &lt;em&gt;Production&lt;/em&gt;). Puedes configurar &lt;strong&gt;reglas de protección&lt;/strong&gt; para que el despliegue a producción requiera la aprobación manual de un líder técnico antes de ejecutarse.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Buenas prácticas para optimizar tus Workflows
&lt;/h2&gt;

&lt;p&gt;A medida que tu proyecto crece, tus &lt;em&gt;workflows&lt;/em&gt; pueden volverse lentos o costosos. Aplica estos consejos de nivel profesional:&lt;/p&gt;

&lt;h3&gt;
  
  
  Uso de Caché para acelerar builds
&lt;/h3&gt;

&lt;p&gt;Instalar dependencias (&lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;pip install&lt;/code&gt;, &lt;code&gt;maven restore&lt;/code&gt;) en cada ejecución consume tiempo valioso. Utiliza el almacenamiento en caché:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Caché de Node modules&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;~/.npm&lt;/span&gt;
    &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}&lt;/span&gt;
    &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;${{ runner.os }}-node-&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Seguridad y permisos mínimos (Principle of Least Privilege)
&lt;/h3&gt;

&lt;p&gt;Restringe los permisos del token automático &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; al inicio del archivo YAML para mitigar riesgos de seguridad:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
  &lt;span class="na"&gt;issues&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Solución de problemas comunes (Troubleshooting)
&lt;/h2&gt;

&lt;p&gt;Incluso a ingenieros DevOps experimentados les fallan los &lt;em&gt;pipelines&lt;/em&gt;. Aquí tienes cómo resolver los problemas más habituales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cómo depurar fallos en los logs
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Activar el modo Debug:&lt;/strong&gt; Crea un secreto en el repositorio llamado &lt;code&gt;ACTIONS_STEP_DEBUG&lt;/code&gt; con el valor &lt;code&gt;true&lt;/code&gt;. Esto generará logs verbosos en la siguiente ejecución.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Revisar artefactos:&lt;/strong&gt; Puedes configurar tu &lt;em&gt;workflow&lt;/em&gt; para que guarde archivos de registro o capturas de pantalla cuando una prueba falle utilizando &lt;code&gt;actions/upload-artifact@v4&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Errores de sintaxis y tipado en archivos YAML
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Error de indentación:&lt;/strong&gt; Usa siempre espacios (recomendado 2 espacios), nunca tabuladores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Uso de comillas:&lt;/strong&gt; Si una cadena contiene caracteres especiales como &lt;code&gt;:&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt; o &lt;code&gt;{}&lt;/code&gt; envuélvela entre comillas dobles: &lt;code&gt;name: "Build: React App"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Herramientas de validación:&lt;/strong&gt; Instala extensiones en tu editor de código como &lt;em&gt;GitHub Actions extension for VS Code&lt;/em&gt; para autocompletado y validación de sintaxis en tiempo real.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Preguntas Frecuentes (FAQ)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿GitHub Actions es completamente gratuito?
&lt;/h3&gt;

&lt;p&gt;Para repositorios públicos, GitHub Actions es &lt;strong&gt;100% gratuito e ilimitado&lt;/strong&gt;. Para repositorios privados, GitHub otorga una cuota mensual de minutos gratuitos (por ejemplo, 2,000 minutos/mes en la cuenta Free). Si superas ese límite, se aplican cobros adicionales por minuto según el sistema operativo del &lt;em&gt;runner&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cuál es la diferencia entre GitHub Actions y Jenkins?
&lt;/h3&gt;

&lt;p&gt;Jenkins es una herramienta &lt;em&gt;open-source&lt;/em&gt; autocontenida que requiere instalación, mantenimiento y plugins manuales en tus propios servidores. GitHub Actions es un servicio gestionado (SaaS) e integrado en GitHub, lo que elimina la sobrecarga operacional de mantener la infraestructura del servidor de CI/CD.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Puedo ejecutar GitHub Actions en mis propios servidores localmente?
&lt;/h3&gt;

&lt;p&gt;Sí. Puedes instalar un &lt;strong&gt;Self-hosted runner&lt;/strong&gt; en cualquier servidor físico, máquina virtual o instancia cloud (AWS, GCP, Azure). Solo debes ir a &lt;em&gt;Settings &amp;gt; Actions &amp;gt; Runners&lt;/em&gt; en tu repositorio y seguir los comandos de instalación proporcionados.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusión
&lt;/h2&gt;

&lt;p&gt;Automatizar tus flujos de desarrollo con este &lt;strong&gt;GitHub Actions tutorial&lt;/strong&gt; es el primer paso firme hacia una cultura DevOps madura. Has aprendido a estructurar archivos YAML, gestionar eventos, asegurar credenciales sensibles con secretos e implementar flujos completos de Integración y Despliegue Continuo.&lt;/p&gt;

&lt;p&gt;La automatización no solo elimina tareas repetitivas, sino que le da a tu equipo la confianza de entregar software de calidad rápidamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;¿Listo para llevar tu carrera DevOps al siguiente nivel?&lt;/strong&gt;&lt;br&gt;
Suscríbete a nuestro boletín técnico para recibir guías avanzadas sobre Kubernetes, Terraform y arquitectura Cloud directamente en tu bandeja de entrada. ¡Comienza automatizando tu repositorio hoy mismo!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>automation</category>
      <category>tech</category>
    </item>
  </channel>
</rss>
