avance
Todavía estamos trabajando en esta característica, ¡pero nos encantaría que la probaras!
Esta característica se proporciona actualmente como parte de un programa de vista previa de conformidad con nuestras políticas de prelanzamiento.
Descripción general
Una definición de flujo de trabajo describe el proceso automatizado que se va a ejecutar. Las definiciones de flujo de trabajo se escriben en YAML utilizando una convención de nomenclatura camelCase. Cada flujo de trabajo consta de:
- Propiedades del esquema: Información básica (nombre, descripción, entradas)
- Pasos: La secuencia de acciones a realizar
- Expresiones: Valores dinámicos utilizando la sintaxis jq
- Secretos: Referencias de credenciales seguras
Conceptos fundamentales
Antes de construir flujos de trabajo, comprenda estos conceptos básicos utilizados en las definiciones de flujo de trabajo.
Cadenas de expresión
Varias propiedades aceptan valores de cadena con expresiones integradas que se evalúan durante la ejecución del flujo de trabajo, lo que permite valores dinámicos en las definiciones del flujo de trabajo.
Las cadenas de expresión pueden contener una o más expresiones, cada una encerrada entre llaves dobles. El contenido dentro de las llaves se evalúa empleando jq, que proporciona poderosas capacidades para acceder y operar con valores.
Ejemplo:
Obtener la longitud de una cadena de entrada de flujo de trabajo:
${{ .workflowInputs.myString | length }}
Para validar y probar sus expresiones, emplee JQ Playground.
Propiedades de expresión
Se puede acceder a varias propiedades con expresiones. Estas propiedades viven en un objeto scope, por lo que las expresiones deben comenzar con un punto (.) para acceder a esas propiedades del objeto de alcance.
Las propiedades disponibles son:
workflowInputs- Objeto que contiene las entradas pasadas al flujo de trabajo al inicio.
Ejemplo:
${{ .workflowInputs.myInput }}
steps- Objeto que contiene una propiedad para cada paso del flujo de trabajosteps.<stepName>- Objeto que contiene propiedades para un paso específicosteps.<stepName>.outputs- Objeto que contiene propiedades de resultado, específicas del paso o acción.
Ejemplo: ${{ .steps.myStep.outputs.myResult }}
Resultados de la evaluación de la expresión
Una sola expresión jq puede evaluar cualquier tipo JSON. Sin embargo, el resultado final de una cadena de expresión depende de si la cadena contiene solo la expresión o contenido adicional.
Expresión única (conserva el tipo JSON):
Si una cadena de expresión consta de una sola expresión sin contenido circundante, se evalúa como el resultado de la expresión jq mientras mantiene su tipo JSON original. Por ejemplo, ${{ .workflowInputs.myArray }} se evalúa como una matriz. Esto es útil para pasar estructuras de datos complejas dentro de un flujo de trabajo.
Expresiones múltiples o contenido mixto (se convierte en cadena):
Si una cadena de expresión contiene contenido distinto a una única expresión, se evalúa como resultado una cadena. Esto ocurre cuando una expresión tiene contenido antes o luego de ella, o cuando la cadena tiene múltiples expresiones dentro de ella. Cada expresión dentro de la cadena se evalúa y se convierte en una representación de cadena.
Importante
Cuando una expresión jq se evalúa como nula, se devuelve un nodo nulo. Por ejemplo, la expresión ${{ .workflowInputs.missingInput }} devuelve nulo si no se proporciona missingInput como entrada del flujo de trabajo.
Ejemplo:
Supongamos que myArray tiene un valor de [1, 2, 3].
Cadena de expresión | Datos de resultados | Tipo de resultado |
|---|---|---|
|
| matriz de números |
| 3 | Número |
| Verdadero | Booleano |
|
| Cadena |
|
| Cadena |
Patrón seguro de expresión
Las propiedades que se pueden emplear en expresiones deben cumplir con: ^[A-Za-z_][A-Za-z0-9_]*$
Referencias secretas
Los valores secretos se pueden usar en acciones a través de cadenas de referencia que especifican el nombre de un secreto que se buscará en el Servicio de Secretos. Para hacer referencia a un secreto en una definición de flujo de trabajo, emplee la siguiente sintaxis:
${{ :secrets:<SECRET_NAME> }}para un secreto que no está en unnamespace${{ :secrets:<NAMESPACE>:<SECRET_NAME> }}para un secreto en unnamespace${{ :secrets:<SCOPE>:<NAMESPACE>:<SECRET_NAME> }}para un secreto en el alcance y namespace. El alcance solo aceptaACCOUNToORGANIZATIONpor ahora.
Una cadena de expresión puede contener una combinación de referencias secretas y expresiones JQ and/or múltiples referencias secretas.
Ejemplos:
steps: - name: mySecretStep type: action action: newrelic.instrumentation.log inputs: message: My message licenseKey: ${{ :secrets:<SECRET_NAME> }}steps: - name: bearer_auth type: action action: utils.http.post inputs: headers: Authorization: Bearer ${{ :secrets:<SECRET_NAME> }}Estructura del esquema
Propiedades del esquema
Propiedad | Obligatorio u opcional | Tipo | Formato | Restricciones | Descripción |
|---|---|---|---|---|---|
| Requerido | Cadena | Debe cumplir con las expresiones regulares
| Longitud máxima : 100 | Los valores
no distinguen entre mayúsculas y minúsculas. Por ejemplo, se considera que
,
y
representan la misma definición de flujo de trabajo. |
| Opcional | Cadena | Debe cumplir con las expresiones regulares
| Longitud máxima : 200 | Un
del flujo de trabajo que describe el propósito del flujo de trabajo. |
| Opcional | Mapa de mapas | Tamaño máximo : 100 | Un mapa de entradas de flujo de trabajo que acepta el flujo de trabajo. Vea las propiedades detalladas a continuación. |
Entradas de flujo de trabajo
Ejemplo:
workflowInputs: myInput1: type: String myInput2: type: Number defaultValue: 42workflowInputs.<inputName>(Obligatorio)- Tipo: Cadena (conforme al patrón seguro para expresiones)
- Longitud mínima: 1
- Longitud máxima: 50
- Descripción: El nombre de la entrada del flujo de trabajo.
workflowInputs.<inputName>.type(Obligatorio)- Tipo: Enumeración (
Boolean, List, Map, String, Int, Float) - Descripción: El tipo de datos de la entrada del flujo de trabajo.
- Tipo: Enumeración (
workflowInputs.<inputName>.defaultValue(Opcional)- Tipo: Cualquiera; debe ajustar a
type. - Descripción: El valor predeterminado para la entrada del flujo de trabajo.
- Tipo: Cualquiera; debe ajustar a
workflowInputs.<inputName>.required(Opcional)- Tipo: Booleano (
True,False). - Descripción: El valor predeterminado de este campo es "Verdadero".
- Tipo: Booleano (
workflowInputs.<inputName>.enumValues(Opcional)- Tipo: Lista (
String). - Descripción: El valor predeterminado de este campo es Lista vacía
{}. Esto es necesario cuando el tipo de workflowInput es Enum.
- Tipo: Lista (
workflowInputs.<inputName>.validations(Opcional)- Tipo: matriz de mapas.
- Descripción: Las validaciones que se deben realizar en las entradas del flujo de trabajo proporcionadas por el usuario. Este es un campo opcional. Las propiedades descritas aquí están presentes en todos los tipos de validación. Los tipos de validación específicos admiten propiedades adicionales como se analiza en la sección Tipos de validación.
validations[*].type(Obligatorio)- Tipo: Cadena
- Descripción: El tipo de validación que indica qué validación se realizará en este campo. Consulte Tipos de validación para obtener más información sobre cada tipo de validación.
validations[*].errorMessage(Obligatorio)- Tipo: Cadena
- Descripción: El mensaje de error que el usuario desea recibir cuando falla una validación particular.
Ejemplo de YAML para validaciones
name: calendar_demo
workflowInputs: timezone: type: String defaultValue: 'America/Los_Angeles' validations: - type: regex errorMessage: "The provided timezone is not correct" pattern: "^[A-Za-z]+\/[A-Za-z_]+(?:\/[A-Za-z_]+)?$"
- type: maxLength errorMessage: "Timezone length should be less than 100" length: 100
accountId: type: Int validations: - type: minIntValue errorMessage: "Account id should be greater than 100000" minValue: 100000 - type: maxIntValue errorMessage: "Account id should be less than 9999999" maxValue: 9999999
steps: - name: getCurrentTime type: action action: http.get version: 1 inputs: url: 'https://worldtimeapi.org/api/timezone/${{ .workflowInputs.timezone }}' selectors: - name: timezone expression: '.responseBody | fromjson.abbreviation' - name: datetime expression: '.responseBody | fromjson.datetime'Tipos de validación
Tipo de validación | Propiedad | Obligatorio u opcional | Tipo | Descripción |
|---|---|---|---|---|
|
| Requerido | Cadena | Valida el valor de entrada del flujo de trabajo contra el patrón de expresión regular proporcionado. |
|
| Requerido | Entero | Valida que el valor de entrada del flujo de trabajo debe ser menor que el valor máximo proporcionado. |
|
| Requerido | Entero | Valida que el valor de entrada del flujo de trabajo debe ser mayor que el valor mínimo proporcionado. |
|
| Requerido | Entero | Valida la longitud máxima de las cadenas de entrada del flujo de trabajo y las colecciones (
). |
Pasos
Propiedad | Obligatorio u opcional | Tipo | Restricciones | Descripción |
|---|---|---|---|---|
| Requerido | matriz de mapas | Los pasos a realizar cuando se ejecuta la definición del flujo de trabajo. Debe haber al menos un paso. Las propiedades descritas aquí están presentes en todos los tipos de pasos. Los tipos de pasos específicos admiten propiedades adicionales como se describe en la sección . |
Importante
Los pasos se ejecutan en el orden en que están definidos en la matriz steps. Si se desea un orden diferente, se puede realizar un jump estableciendo la propiedad steps[*].next en el nombre del paso deseado para saltar.
Propiedades comunes de los pasos
Propiedad | Obligatorio u opcional | Tipo | Formato | Restricciones | Descripción |
|---|---|---|---|---|---|
| Requerido | Cadena | Debe cumplir con el no puede ser
. | Longitud máxima: 100 | El nombre del paso al que hará referencia
. No pueden ser las palabras clave especiales
,
o
, ya que se emplean para indicar un paso final, una continuación de un bucle o para salir de un bucle. |
| Requerido | Cadena | El tipo de paso, que indica qué hace el paso cuando se ejecuta. Consulte los a continuación para conocer las opciones disponibles. | ||
| Opcional | Cadena | Debe cumplir con el patrón de expresión segura | El nombre del siguiente paso a ejecutar cuando este paso se complete exitosamente. La palabra clave especial end se puede emplear para indicar que este paso debe ser el último en ejecutar. Si se omite
, la siguiente entrada en la matriz de definición
se empleará como el siguiente paso implícito. Si no hay ninguna entrada siguiente, se completará el flujo de trabajo. | |
| Opcional | Booleano |
es una opción de configuración dentro de un paso de un flujo de trabajo. El valor predeterminado es falso. Al establecer
en
, el flujo de trabajo garantiza que cualquier error encontrado durante la ejecución de este paso no provocará que todo el flujo de trabajo falle. En su lugar, el flujo de trabajo continuará ejecutando los pasos siguientes. |
Tipos de pasos
Acción
Un paso que ejecuta una acción específica. Consulte el catálogo de acciones para conocer las opciones disponibles.
steps[*].action(Obligatorio)- Tipo: Cadena
- Descripción: El nombre completo de la función de acción que se va a ejecutar. Deberá seguir la siguiente convención:
<company domain>.<category of work>.<action name in camelCase>
Ejemplo:
Acción que emplea los servicios de New Relic (por ejemplo, a través de NerdGraph):
newrelic.dashboards.getDashboardAcción mediante Slack:
slack.chat.postMessagesteps[*].version(Obligatorio)- Tipo: Cadena
- Descripción: La versión de la función de acción que se va a ejecutar.
steps[*].inputs(Opcional)Tipo: Mapa de valores (incluye expresiones)
Descripción:
- Los datos de entrada que se pasarán a la función de acción. Las entradas específicas aceptadas se definen para cada acción.
- Las entradas pueden emplear expresiones. Consulte la sección Cadenas de expresión para obtener más detalles.
Importante
No se deben pasar datos sensibles (ni claves de API ni secretos, ni PII, PHI ni ningún dato de identificación personal) como argumentos.
steps[*].inputs.selectors(Opcional)Tipo: lista de mapas en formato
nameconexpression.Descripción:
- La entrada
selectorsle permite redefinir la salida para devolver solo los elementos especificados. - Se pueden emplear expresiones. Consulte la sección Cadenas de expresión para obtener más detalles.
- La entrada
Ejemplo:
- En el ejemplo dado, estamos obteniendo
timezoneydatetimecomo respuesta de la acción http.get.
name: calendar_demo
workflowInputs: timezone: type: String defaultValue: 'America/Los_Angeles' accountId: type: Int
steps: - name: getCurrentTime type: action action: http.get version: 1 inputs: url: 'https://worldtimeapi.org/api/timezone/${{ .workflowInputs.timezone }}' selectors: - name: timezone expression: '.responseBody | fromjson.abbreviation' - name: datetime expression: '.responseBody | fromjson.datetime'Bucle
Un bucle itera sobre colecciones (listas, mapas, arrays) y crea automáticamente las variables index y element para cada iteración. Estas variables de bucle son accesibles solo dentro del bucle usando ${{ .steps.<loopStepName>.loop.element }} o ${{ .steps.<loopStepName>.loop.index }}
Propiedades:
steps[*].for(Obligatorio)- Tipo: Constante
- Descripción: Señala el inicio de un bucle
steps[*].in(Obligatorio)- Tipo: Cadena (expresión)
- Descripción: Expresión que se evalúa como una colección de elementos
steps[*].steps(Obligatorio)- Descripción: Pasos ejecutados en cada iteración. Puede incluir cualquier tipo de paso, incluidos los bucles anidados.
Importante
- for: (requerido). Este es el elemento de nivel superior que indica el comienzo de un bucle for.
- in: (requerido). Para que la colección de entrada se itere, debe ser convertible a una matriz de Java.
- pasos: (requerido). En cada iteración se ejecutarán los pasos.
- elemento e índice se asignan automáticamente como parte del bucle.
indexes de base cero,elementpuede ser un tipo complejo si tiene una colección de elementos complejos.- Las variables creadas dentro del bucle (variables de bucle y salidas de paso) solo son accesibles dentro del bucle
- Estas variables se borran cuando el bucle sale y serán nulas si se accede a ellas fuera del bucle.
- Los bucles pueden acceder a variables definidas fuera del bucle.
Ejemplo: Bucle básico
name: myWorkflow steps: - name: loopStep type: loop for: in: '${{ .workflowInputs.count }}' steps: - name: step1 type: action action: internal.example.sayHello version: '1' inputs: name: 'Element : ${{ .steps.loopStep.loop.element }} , Index : ${{ .steps.loopStep.loop.index }}' # not exist outside of this loopEjemplo: Bucle en enteros
name: myWorkflow steps: - name: loopStep type: loop for: in: ${{ [range(1; 6)] }} steps: - name: step1 type: action action: internal.example.sayHello version: '1' inputs: name: 'Element: ${{ .steps.loopStep.loop.element }} , Index : ${{ .steps.loopStep.loop.index }}' # Element : [1, 2, 3, 4, 5] , Index : [0, 1, 2, 3, 4]Ejemplo: Bucle en mapa
name: myWorkflow steps: - name: loopStep type: loop for: in: '${{ [ { "key1": "val1" }, { "key2": "val2"} ] }}' steps: - name: step1 type: action action: internal.example.sayHello version: '1' inputs: name: 'Element : ${{ .steps.loopStep.loop.element }} , Index : ${{ .steps.loopStep.loop.index }}' # Element: [{ "key1": "val1" }, { "key2": "val2"}] , Index : [0, 1]Ejemplo: Saltar dentro de un bucle
Se permite saltar entre pasos dentro del mismo bucle. No se permite saltar dentro/fuera de bucles, entre diferentes bucles o a bucles padre/hijo.
name: myWorkflow steps: - name: firstStep type: action action: example.messaging.sayHello version: '1.0.0' - name: loopStep type: loop for: in: '${{ .workflowInputs.count }}' steps: - name: step1 type: action action: example.messaging.sayHello version: '1.0.0' inputs: name: '${{ .steps.loopStep.loop.element }}' next: step3 # Okay within the loop - name: step2 type: action action: example.messaging.sayHello version: '1.0.0' inputs: name: '${{ .steps.step1.outputs.greeting }}' - name: step3 type: action action: example.messaging.sayHello version: '1.0.0' next: firstStep # Not okay, first step is not in the loop contextEjemplo: Break y continue
Utilice next: break o next: continue para controlar el flujo del bucle. Estas son palabras clave reservadas dentro de los bucles. Fuera de los bucles, saltan al final del flujo de trabajo. Nota: end se comporta como break dentro de los bucles.
name: myWorkflow steps: - name: loopStep type: loop for: in: '${{ [range(1; 6)] }}' steps: - name: insideLoopStep1 type: action action: example.messaging.sayHello version: '1.0.0' inputs: name: '${{ .steps.loopStep.loop.element }}' next: continue - name: insideLoopStep2 type: action action: example.messaging.sayHello version: '1.0.0' inputs: name: '${{ .steps.loopStep.loop.element }}' - name: loopStepAgain type: loop for: in: '${{ .workflowInputs.count }}' steps: - name: switchStep type: switch switch: - condition: '${{ .steps.loopStepAgain.loop.index >= 0 }}' next: break - name: insideLoopStepAgain type: action action: example.messaging.sayHello version: '1.0.0' inputs: name: '${{ .steps.loopStepAgain.loop.element }}'Ejemplo sencillo:
name: myWorkflow steps: - name: loopStep type: loop for: in: "${{ [range(1; 5)] }}" steps: - name: step1 type: action action: newrelic.ingest.sendLogs version: 1 inputs: logs: - message: "Loop: ${{ .steps.loopStep.loop.element }}"Cambiar
Un paso que comprueba varias condiciones y toma la primera rama que se evalúa como verdadera.
Un conmutador puede contener cualquier número de
conditionelementos en una lista. Comprobará las condiciones en orden y procesará la primera que evalúe como verdadera. Si ninguno se evalúa como verdadero, ejecutará su pasonextcomo se define ensteps[*].next.steps[*].switch(Obligatorio)- Tipo: matriz
- Descripción: Una matriz de casos switch, que especifica la lista ordenada de condiciones a evaluar.
steps[*].switch[*].condition(Obligatorio)- Tipo: Cadena (expresión)
- Descripción: El estado de la caja del interruptor. Si se evalúa como verdadero, se ejecutará el paso del caso
next. - Consulte la sección Cadenas de expresión para obtener más detalles.
steps[*].switch[*].next(Obligatorio)- Tipo: Cadena (conforme al patrón seguro para expresiones)
- Descripción: El nombre del paso a ejecutar si la condición del caso se evalúa como verdadera. La palabra clave especial
endse puede emplear para indicar que este paso debe ser el último en ejecutar.
- name: hasCompletedtype: switchswitch:- condition: ${{ .steps.waitForCompletion.outputs.automationExecutionStatus == "Failed" }}next: displayError- condition: ${{ .steps.waitForCompletion.outputs.automationExecutionStatus == "Success" }}next: displaySuccessnext: displayUnexpected
Esperar
Un paso que pausa la ejecución del flujo de trabajo durante una cantidad específica de segundos antes de continuar.
El paso de espera también puede escuchar una o más señales. Cada señal debe tener un siguiente paso correspondiente definido en una lista. Si se recibe una señal durante el periodo de espera, se procesará la primera señal recibida y el flujo de trabajo ejecutará el siguiente paso definido. Si no se recibe ninguna señal, el flujo de trabajo continúa normalmente una vez finalizado el periodo de espera.
El valor recibido de una señal se almacena en la salida del paso de espera y se puede emplear en pasos posteriores para lógica o procesamiento.
Ejemplo:
name: waitSignalExample workflowInputs: steps: - name: waitStep type: wait seconds: 300 signals: [{name: 'mySignalName', next: 'firstStep'}] - name: endStep type: action action: newrelic.instrumentation.log version: 1 inputs: message: "didn't get signal" next: end - name: firstStep type: action action: newrelic.instrumentation.log version: 1 inputs: message: ${{ .steps.waitStep.outputs.signalInputs.myString }}steps[*].seconds(Obligatorio)- Tipo: Número
- Descripción: Número de segundos que se deben esperar antes de continuar con la ejecución del flujo de trabajo.
steps[*].signals- Tipo: matriz
- Descripción: Las señales que, al ser recibidas, desviarán el flujo del programa.
steps[*].signals[*].name- Tipo: Cadena
- Descripción: El nombre de la señal que se debe escuchar.
steps[*].signals[*].next- Tipo: Cadena
- Descripción: Paso a ejecutar si se recibe la señal especificada.
Asignar
Un paso que define variables para usar durante todo el flujo de trabajo. Este paso asigna valores a variables que pueden referenciar en pasos posteriores. Al definir todas las variables en un solo lugar, este tipo de paso hace que el flujo de trabajo sea más legible y optimizado.
Ejemplo de flujo de trabajo:
name: sampleWorkflowWithAssign description: WorkflowAssignDemo
workflowInputs: initialValue: type: String anotherValue: type: Int
steps: - name: runAction type: action action: internal.http.post version: 1 inputs: url: 'http://localhost:8505/tasks/gc' # temporal-activity-worker-java service port selectors: - name: statusCode expression: '.statusCode' - name: responseBody expression: '.responseBody' - name: variableInitialization type: assign inputs: stringVar: "${{ .workflowInputs.initialValue }}" intVar: "${{ .workflowInputs.anotherValue }}" concatenationVar: "${{ .workflowInputs.initialValue }} - concatenated" booleanVar: true mapVar: key1: "value1" key2: "${{ .workflowInputs.initialValue }}" listVar: - "listItem1" - "${{ .workflowInputs.initialValue }}" - "${{ .workflowInputs.anotherValue }}" statusCode: ${{ .steps.runAction.outputs.statusCode }}
- name: wait type: wait seconds: 2
- name: logVariables type: action action: newrelic.ingest.sendLogs version: 1 inputs: logs: - message: "stringVar: ${{ .steps.variableInitialization.outputs.stringVar }}" - message: "intVar: ${{ .steps.variableInitialization.outputs.intVar }}" - message: "concatenationVar: ${{ .steps.variableInitialization.outputs.concatenationVar }}" - message: "booleanVar: ${{ .steps.variableInitialization.outputs.booleanVar }}" - message: "mapVar: ${{ .steps.variableInitialization.outputs.mapVar | tojson }}" - message: "listVar: ${{ .steps.variableInitialization.outputs.listVar | tojson }}" - message: "statusCode: ${{ .steps.variableInitialization.outputs.statusCode }}"steps[*].inputs(Obligatorio)- Tipo: Mapa de valores (incluye expresiones)
- Descripción:
- Las entradas son un mapa de nombres de variables y sus valores asignados. Cuando se asignan referencias secretas a variables, permanecen como referencias secretas y no se convierten a sus valores reales. Sin embargo, otras expresiones (como las entradas del flujo de trabajo) se evalúan y se convierten a sus valores reales.
- Tipos de entrada permitidos:
Integer,Double,Boolean,String,Array,Map
Ejemplo completo
Demostración del calendario
Un ejemplo completo de flujo de trabajo que demuestra múltiples características de flujo de trabajo que incluyen entradas de flujo de trabajo, acciones HTTP, selectores, pasos de espera, consulta NRQL, declaraciones de cambio y notificaciones de Slack.
name: calendar_demo
workflowInputs: timezone: type: String defaultValue: 'America/Los_Angeles' accountId: type: Int
steps: - name: getCurrentTime type: action action: http.get version: 1 inputs: url: 'https://worldtimeapi.org/api/timezone/${{ .workflowInputs.timezone }}' selectors: - name: timezone expression: '.responseBody | fromjson.abbreviation' - name: datetime expression: '.responseBody | fromjson.datetime'
- name: logTime type: action action: newrelic.instrumentation.log version: 1 inputs: message: 'DEMO: In the ${{ .steps.getCurrentTime.outputs.timezone }} timezone, the current time is ${{ .steps.getCurrentTime.outputs.datetime }}' licenseKey: ${{ :secrets:STAGING_NEW_RELIC_LICENSE_KEY }}
- name: wait type: wait seconds: 1
- name: queryForLog type: action action: newrelic.nrql.query version: 1 inputs: accountIds: ['${{ .workflowInputs.accountId }}'] query: FROM Log SELECT * WHERE message LIKE 'DEMO:%${{ .steps.getCurrentTime.outputs.datetime }}'
- name: checkQuery type: switch switch: - condition: ${{ .steps.queryForLog.outputs.results | length > 0 }} next: postResultsMessage
- name: postWaitingMessage type: action action: slack.chat.postMessage version: 1 inputs: channel: test-channel-workflow text: Waiting for log message... token: ${{ :secrets:dn_staging_slack_token }} next: wait
- name: postResultsMessage type: action action: slack.chat.postMessage version: 1 inputs: channel: test-channel-workflow text: 'Found log message! ${{ .steps.queryForLog.outputs.results[0].message }}' token: ${{ :secrets:dn_staging_slack_token }}