Skip to main content

Documentation Index

Fetch the complete documentation index at: https://wiki.vivla.com/llms.txt

Use this file to discover all available pages before exploring further.

Canal

Todas las notificaciones van al canal #deploys (C091W026C23). Este canal centraliza el estado de deploys, builds, y procesos automatizados de todos los repositorios.

Formato estándar

Usamos Block Kit dentro de attachments para lograr:
  • Color lateral verde/rojo según resultado
  • Título con emoji + nombre del proyecto + resultado
  • Campos estructurados con info relevante (repo, branch, commit, etc.)
  • Link al workflow en el footer
  • Fallback text para que el preview en móvil/desktop se vea bien
El campo text dentro del attachment es obligatorio para que el preview de la notificación (push móvil, desktop) muestre contenido útil en vez de “sent an attachment”.

GitHub Actions: Dos enfoques

En GitHub Actions hay dos formas de enviar notificaciones a Slack. Cada una tiene restricciones diferentes.

Opción A: Incoming Webhook (actual)

Usa un Incoming Webhook URL. Es la opción más simple de configurar. Secret necesario: SLACK_WEBHOOK_URL
Los Incoming Webhooks de Slack NO soportan header blocks dentro de attachments. Usar type: "header" dentro de un attachment causa el error invalid_attachments. En su lugar, usar un section con mrkdwn en bold para el título.
- name: Send Slack notification
  if: always()
  env:
    SLACK_WEBHOOK_URL: $\{{ secrets.SLACK_WEBHOOK_URL }}
  run: |
    if [ -z "$SLACK_WEBHOOK_URL" ]; then
      echo "Slack webhook not configured, skipping notification"
      exit 0
    fi

    # -- Determinar resultado --
    if [ "$JOB_FAILED" = "false" ]; then
      COLOR="#36a64f"
      STATUS_EMOJI="✅"
      RESULT="succeeded"
    else
      COLOR="#e01e5a"
      STATUS_EMOJI="❌"
      RESULT="failed"
    fi

    COMMIT_SHORT=$(echo "$\{{ github.sha }}" | cut -c1-7)
    PROJECT_NAME="Mi Proyecto"  # Cambiar por el nombre del proyecto
    HEADER="$STATUS_EMOJI $PROJECT_NAME deploy $RESULT"

    PAYLOAD=$(jq -n \
      --arg color "$COLOR" \
      --arg header "$HEADER" \
      --arg fallback "$HEADER ($\{{ github.ref_name }} @ $COMMIT_SHORT)" \
      --arg repo "$\{{ github.repository }}" \
      --arg branch "$\{{ github.ref_name }}" \
      --arg commit "$COMMIT_SHORT" \
      --arg author "$\{{ github.actor }}" \
      --arg url "$\{{ github.server_url }}/$\{{ github.repository }}/actions/runs/$\{{ github.run_id }}" \
      '{attachments:[{color:$color,text:$fallback,blocks:[
        {type:"section",text:{type:"mrkdwn",text:("*" + $header + "*")}},
        {type:"section",fields:[
          {type:"mrkdwn",text:("*Repo:*\n"+$repo)},
          {type:"mrkdwn",text:("*Branch:*\n`"+$branch+"`")},
          {type:"mrkdwn",text:("*Commit:*\n`"+$commit+"`")},
          {type:"mrkdwn",text:("*Author:*\n"+$author)}
        ]},
        {type:"context",elements:[
          {type:"mrkdwn",text:("<"+$url+"|View workflow run>")}
        ]}
      ]}]}')

    curl -sf -X POST "$SLACK_WEBHOOK_URL" \
      -H "Content-Type: application/json" \
      -d "$PAYLOAD"

Opción B: Slack Web API (recomendada)

Usa la Slack Web API (chat.postMessage) directamente con un Bot Token. Soporta todo Block Kit incluyendo header blocks dentro de attachments, igual que Windmill. Secret necesario: SLACK_BOT_TOKEN (formato xoxb-...)
- name: Send Slack notification
  if: always()
  env:
    SLACK_BOT_TOKEN: $\{{ secrets.SLACK_BOT_TOKEN }}
  run: |
    if [ -z "$SLACK_BOT_TOKEN" ]; then
      echo "Slack bot token not configured, skipping notification"
      exit 0
    fi

    # -- Determinar resultado --
    if [ "$JOB_FAILED" = "false" ]; then
      COLOR="#36a64f"
      STATUS_EMOJI="✅"
      RESULT="succeeded"
    else
      COLOR="#e01e5a"
      STATUS_EMOJI="❌"
      RESULT="failed"
    fi

    COMMIT_SHORT=$(echo "$\{{ github.sha }}" | cut -c1-7)
    PROJECT_NAME="Mi Proyecto"  # Cambiar por el nombre del proyecto
    HEADER="$STATUS_EMOJI $PROJECT_NAME deploy $RESULT"
    CHANNEL="C091W026C23"  # #deploys

    PAYLOAD=$(jq -n \
      --arg channel "$CHANNEL" \
      --arg color "$COLOR" \
      --arg header "$HEADER" \
      --arg fallback "$HEADER ($\{{ github.ref_name }} @ $COMMIT_SHORT)" \
      --arg repo "$\{{ github.repository }}" \
      --arg branch "$\{{ github.ref_name }}" \
      --arg commit "$COMMIT_SHORT" \
      --arg author "$\{{ github.actor }}" \
      --arg url "$\{{ github.server_url }}/$\{{ github.repository }}/actions/runs/$\{{ github.run_id }}" \
      '{channel:$channel,text:$fallback,attachments:[{color:$color,text:$fallback,blocks:[
        {type:"header",text:{type:"plain_text",text:$header,emoji:true}},
        {type:"section",fields:[
          {type:"mrkdwn",text:("*Repo:*\n"+$repo)},
          {type:"mrkdwn",text:("*Branch:*\n`"+$branch+"`")},
          {type:"mrkdwn",text:("*Commit:*\n`"+$commit+"`")},
          {type:"mrkdwn",text:("*Author:*\n"+$author)}
        ]},
        {type:"context",elements:[
          {type:"mrkdwn",text:("<"+$url+"|View workflow run>")}
        ]}
      ]}]}')

    curl -sf -X POST "https://slack.com/api/chat.postMessage" \
      -H "Content-Type: application/json" \
      -H "Authorization: Bearer $SLACK_BOT_TOKEN" \
      -d "$PAYLOAD"

Comparación

Webhook (Opción A)Web API (Opción B)
SetupCrear webhook URL en Slack appBot token de la Slack app
SecretSLACK_WEBHOOK_URLSLACK_BOT_TOKEN
Header blocks❌ No soportado en attachments✅ Soportado
CanalFijo al crear el webhookConfigurable por request
Consistencia con WindmillDiferente formato✅ Mismo formato
Si ya tienes una Slack app con bot token (como la que usa Windmill), la Opción B es preferible porque usa el mismo formato en todos los sistemas y soporta Block Kit completo.

Windmill (TypeScript con Slack Web API)

import { WebClient } from "@slack/web-api";

const CHANNEL_ID = "C091W026C23";

interface SlackNotifyParams {
  success: boolean;
  projectName: string;
  details: Record<string, string>; // key-value pairs para los fields
  linkUrl?: string;
  linkLabel?: string;
}

export async function sendSlackNotification(
  slackToken: string,
  params: SlackNotifyParams
) {
  const web = new WebClient(slackToken);

  const color = params.success ? "#36a64f" : "#e01e5a";
  const emoji = params.success ? "✅" : "❌";
  const result = params.success ? "succeeded" : "failed";
  const headerText = `${emoji} ${params.projectName} ${result}`;

  const fields = Object.entries(params.details).map(([key, value]) => ({
    type: "mrkdwn" as const,
    text: `*${key}:*\n${value}`,
  }));

  const contextElements = [
    {
      type: "mrkdwn" as const,
      text: `Executed at: ${new Date().toISOString()}`,
    },
  ];

  if (params.linkUrl) {
    contextElements.push({
      type: "mrkdwn" as const,
      text: `<${params.linkUrl}|${params.linkLabel || "View details"}>`,
    });
  }

  await web.chat.postMessage({
    channel: CHANNEL_ID,
    text: headerText, // Fallback para preview
    attachments: [
      {
        color,
        text: headerText,
        blocks: [
          {
            type: "section",
            text: { type: "mrkdwn", text: `*${headerText}*` },
          },
          ...(fields.length > 0
            ? [{ type: "section", fields }]
            : []),
          {
            type: "context",
            elements: contextElements,
          },
        ],
      },
    ],
  });
}
Ejemplo de uso en Windmill:
await sendSlackNotification(slack_auth.token, {
  success: true,
  projectName: "Windmill Sync",
  details: {
    "Process": "Daily sync",
    "Records": "1,234 synced",
  },
});

Campos por proyecto

Cada proyecto incluye los campos más relevantes en la sección fields. Máximo 6 campos (límite de Slack para el layout de 2 columnas).
ProyectoCampos
vivla-toolsRepo, Branch, Commit, Author, Railway Backend, Vercel Frontend
vivla-mobileRepo, Branch, Commit, Author + campos de build por plataforma
Windmill scriptsProcess, Result/Records + campos específicos del proceso
Para builds con múltiples jobs (como mobile con iOS/Android), agrega los estados como campos adicionales usando emojis de estado: ✅ success, ❌ failed, ⏭️ skipped, 🚀 deployed.

Reglas

  1. Siempre usar attachments con color y blocks — nunca solo text o solo blocks a nivel root.
  2. Siempre incluir text dentro del attachment como fallback para el preview.
  3. Título: emoji de estado + nombre del proyecto + resultado (succeeded/failed). Con Webhook usar section + mrkdwn bold. Con Web API se puede usar header block.
  4. Section fields: datos estructurados en pares clave-valor.
  5. Context: timestamp y/o link al workflow/proceso.
  6. Colores: #36a64f (verde) para éxito, #e01e5a (rojo) para fallo.