local workflow = require("assay.engine.workflow")
local c = workflow.client({
engine_url = env.get("ASSAY_ENGINE_URL") or "http://localhost:8080",
})
local STEP_NAMES = { "Approval", "Tag & Retag", "GitOps Update", "ArgoCD Sync", "Health Check" }
local function init_steps()
local s = {}
for i, name in ipairs(STEP_NAMES) do
s[i] = {
name = name,
status = i == 1 and "running" or "waiting",
started_at = i == 1 and os.date("!%Y-%m-%dT%H:%M:%SZ") or nil,
}
end
return s
end
local function add_log(state, msg, idx)
state.log[#state.log + 1] = {
time = os.date("!%H:%M:%S"),
msg = msg,
step = idx or state.current_step,
}
end
local APPROVAL_TIMEOUT_SECS = 600
c:register_workflow("DemoPipeline", function(ctx, input)
local now_unix = os.time()
local state = {
status = "pending_approval",
current_step = 1,
steps = init_steps(),
log = {},
}
state.steps[1].actions = { "approve", "reject" }
state.steps[1].expires_at = now_unix + APPROVAL_TIMEOUT_SECS
ctx:register_query("pipeline_state", function() return state end)
add_log(state, "Pipeline started — waiting for approval (timeout in "
.. (APPROVAL_TIMEOUT_SECS / 60) .. " min)", 1)
local sig = ctx:wait_for_signal("step_action", { timeout = APPROVAL_TIMEOUT_SECS })
state.steps[1].actions = nil
state.steps[1].expires_at = nil
if not sig then
state.steps[1].status = "cancelled"
state.steps[1].completed_at = os.date("!%Y-%m-%dT%H:%M:%SZ")
state.status = "cancelled"
for i = 2, #state.steps do state.steps[i].status = "skipped" end
add_log(state, "Approval timed out after "
.. (APPROVAL_TIMEOUT_SECS / 60) .. " min — auto-cancelled", 1)
ctx:cancel("approval timed out after " .. APPROVAL_TIMEOUT_SECS .. "s")
return
end
if sig.action ~= "approve" then
state.steps[1].status = "cancelled"
state.steps[1].completed_at = os.date("!%Y-%m-%dT%H:%M:%SZ")
state.status = "cancelled"
for i = 2, #state.steps do state.steps[i].status = "skipped" end
add_log(state, "Pipeline rejected by " .. (sig.user or "unknown"), 1)
ctx:cancel("rejected by " .. (sig.user or "unknown"))
return
end
state.steps[1].status = "done"
state.steps[1].completed_at = os.date("!%Y-%m-%dT%H:%M:%SZ")
add_log(state, "Approved by " .. (sig.user or "unknown"), 1)
local ok, err = pcall(function()
for i = 2, #state.steps do
state.current_step = i
state.steps[i].status = "running"
state.steps[i].started_at = os.date("!%Y-%m-%dT%H:%M:%SZ")
add_log(state, "Starting " .. state.steps[i].name, i)
for tick = 1, 3 do
ctx:sleep(2)
add_log(state, state.steps[i].name .. " progress " .. (tick * 33) .. "%", i)
end
state.steps[i].status = "done"
state.steps[i].completed_at = os.date("!%Y-%m-%dT%H:%M:%SZ")
add_log(state, state.steps[i].name .. " complete", i)
end
end)
if not ok then
if tostring(err):find("__ASSAY_WORKFLOW_CANCELLED__", 1, true) then
local at = state.current_step or 1
if state.steps[at] and state.steps[at].status == "running" then
state.steps[at].status = "cancelled"
state.steps[at].completed_at = os.date("!%Y-%m-%dT%H:%M:%SZ")
add_log(state, state.steps[at].name .. " cancelled by operator", at)
end
for j = (at or 1) + 1, #state.steps do
if state.steps[j].status == "waiting" then
state.steps[j].status = "skipped"
end
end
state.status = "cancelled"
error(err, 0)
end
error(err, 0)
end
state.status = "done"
add_log(state, "Pipeline complete", #state.steps)
end)
c:listen({ queue = "demo-q", namespace = "demo" })