Monorepo for Tangled
0

Configure Feed

Select the types of activity you want to include in your feed.

appview/pages,appview/pipelines: refresh workflow UI

Signed-off-by: dawn <dawn@tangled.org>

dawn (Jul 9, 2026, 12:52 PM +0300) b9165c8d 54e2d3c2

+195 -126
+5 -5
appview/pages/pages.go
··· 1667 1667 1668 1668 type WorkflowParams struct { 1669 1669 BaseParams 1670 - RepoInfo repoinfo.RepoInfo 1671 - Pipeline types.Pipeline 1672 - Workflow string 1673 - LogUrl string 1674 - Active string 1670 + RepoInfo repoinfo.RepoInfo 1671 + Pipeline types.Pipeline 1672 + Workflow string 1673 + SSHLogCommand string 1674 + Active string 1675 1675 } 1676 1676 1677 1677 func (p *Pages) Workflow(w io.Writer, params WorkflowParams) error {
+14 -7
appview/pages/templates/repo/pipelines/fragments/logBlock.html
··· 1 1 {{ define "repo/pipelines/fragments/logBlock" }} 2 2 <div id="lines" hx-swap-oob="beforeend"> 3 - <details id="step-{{ .Id }}" {{if not .Collapsed}}open{{end}} class="group pb-2 rounded-sm border border-gray-200 dark:border-gray-700"> 4 - <summary class="sticky top-0 pt-2 px-2 group-open:pb-2 group-open:mb-2 list-none cursor-pointer group-open:border-b border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 hover:text-gray-500 hover:dark:text-gray-400"> 5 - <div class="group-open:hidden flex items-center gap-1">{{ i "chevron-right" "w-4 h-4" }} {{ template "stepHeader" . }}</div> 6 - <div class="hidden group-open:flex items-center gap-1">{{ i "chevron-down" "w-4 h-4" }} {{ template "stepHeader" . }}</div> 3 + <details id="step-{{ .Id }}" {{if not .Collapsed}}open{{end}} class="group overflow-hidden rounded border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800"> 4 + <summary class="list-none cursor-pointer transition-colors hover:bg-gray-50 hover:text-gray-600 dark:hover:bg-gray-700/40 dark:hover:text-gray-300"> 5 + <div class="flex items-center gap-2 px-3 py-3 group-open:hidden"> 6 + {{ i "chevron-right" "size-4 shrink-0" }} {{ template "stepHeader" . }} 7 + </div> 8 + <div class="hidden items-center gap-2 px-3 py-3 group-open:flex"> 9 + {{ i "chevron-down" "size-4 shrink-0" }} {{ template "stepHeader" . }} 10 + </div> 7 11 </summary> 8 - <div class="font-mono whitespace-pre overflow-x-auto px-2"><div class="text-blue-600 dark:text-blue-300">{{ .Command }}</div><div id="step-body-{{ .Id }}"></div></div> 12 + <div class="overflow-x-auto border-t border-gray-200 bg-gray-50 px-3 py-3 font-mono text-sm leading-relaxed dark:border-gray-700 dark:bg-gray-800"> 13 + {{ if .Command }}<div class="text-blue-600 dark:text-blue-300">{{ .Command }}</div>{{ end }} 14 + <div id="step-body-{{ .Id }}" class="text-gray-900 dark:text-gray-100"></div> 15 + </div> 9 16 </details> 10 17 </div> 11 18 {{ end }} 12 19 13 20 {{ define "stepHeader" }} 14 - {{ .Name }} 15 - <span class="ml-auto text-sm text-gray-500 tabular-nums" data-timer="{{ .Id }}" data-start="{{ .StartTime.Unix }}"></span> 21 + <span class="truncate text-base font-semibold text-gray-900 dark:text-gray-100">{{ .Name }}</span> 22 + <span class="ml-auto text-sm tabular-nums text-gray-500 dark:text-gray-400" data-timer="{{ .Id }}" data-start="{{ .StartTime.Unix }}"></span> 16 23 {{ end }}
+1 -1
appview/pages/templates/repo/pipelines/fragments/logBlockEnd.html
··· 1 1 {{ define "repo/pipelines/fragments/logBlockEnd" }} 2 2 <span 3 - class="ml-auto text-sm text-gray-500 tabular-nums" 3 + class="ml-auto text-sm tabular-nums text-gray-500 dark:text-gray-400" 4 4 data-timer="{{ .Id }}" 5 5 data-start="{{ .StartTime.Unix }}" 6 6 data-end="{{ .EndTime.Unix }}"
+1 -1
appview/pages/templates/repo/pipelines/fragments/logLine.html
··· 1 1 {{ define "repo/pipelines/fragments/logLine" }} 2 - <div id="step-body-{{ .Id }}" hx-swap-oob="beforeend" class="whitespace-pre"><p>{{ .Content }}</p></div> 2 + <div id="step-body-{{ .Id }}" hx-swap-oob="beforeend"><p class="whitespace-pre">{{ .Content }}</p></div> 3 3 {{ end }} 4 4
+3 -3
appview/pages/templates/repo/pipelines/fragments/workflowSymbol.html
··· 9 9 {{ $icon = "circle-dashed" }} 10 10 {{ $color = "text-yellow-600 dark:text-yellow-500" }} 11 11 {{ else if eq $kind "running" }} 12 - {{ $icon = "circle-dashed" }} 13 - {{ $color = "text-yellow-600 dark:text-yellow-500" }} 12 + {{ $icon = "refresh-cw" }} 13 + {{ $color = "text-gray-700 dark:text-gray-100" }} 14 14 {{ else if eq $kind "success" }} 15 15 {{ $icon = "check" }} 16 - {{ $color = "text-green-600 dark:text-green-500" }} 16 + {{ $color = "text-green-600 dark:text-green-400" }} 17 17 {{ else if eq $kind "cancelled" }} 18 18 {{ $icon = "circle-slash" }} 19 19 {{ $color = "text-gray-600 dark:text-gray-500" }}
+120 -88
appview/pages/templates/repo/pipelines/workflow.html
··· 6 6 {{ template "repo/fragments/og" (dict "RepoInfo" .RepoInfo "Title" $title "Url" $url) }} 7 7 {{ end }} 8 8 9 - {{ define "repoContent" }} 10 - <section class="w-full grid grid-cols-1 md:grid-cols-4 gap-2 mt-2"> 11 - <div class="col-span-1"> 12 - {{ block "sidebar" . }} {{ end }} 9 + {{ define "repoContentLayout" }} 10 + <section class="w-full rounded bg-white py-4 pl-5 pr-8 dark:bg-gray-800"> 11 + <div class="grid grid-cols-1 gap-[14px] lg:grid-cols-[240px_minmax(0,1fr)]"> 12 + <div class="min-w-0"> 13 + {{ template "sidebar" . }} 14 + </div> 15 + <div class="min-w-0"> 16 + {{ template "repoContent" . }} 17 + </div> 13 18 </div> 14 - <div class="col-span-1 md:col-span-3"> 15 - {{ if $.RepoInfo.Roles.IsPushAllowed }} 16 - {{ $status := (index .Pipeline.Statuses .Workflow).Latest.Status }} 17 - <div class="flex justify-between items-center mb-2"> 18 - <button 19 - class="btn group" 20 - hx-post="/{{ $.RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/retry" 21 - hx-swap="none" 22 - hx-disabled-elt="this" 23 - {{ if .Pipeline.InProgress -}} 24 - disabled 19 + </section> 20 + {{ template "fragments/workflow-timers" }} 21 + {{ end }} 22 + 23 + {{ define "repoContent" }} 24 + {{ with (index .Pipeline.Statuses .Workflow).Latest }} 25 + {{ if .Error }} 26 + <div class="mb-4 flex items-start gap-2 rounded border border-red-200 bg-red-50/60 p-3 text-sm text-red-700 dark:border-red-900/60 dark:bg-red-950/20 dark:text-red-400"> 27 + {{ i "triangle-alert" "size-4 shrink-0 mt-0.5" }} 28 + <div class="min-w-0 flex-1"> 29 + <div class="break-words whitespace-pre-wrap">{{- .ErrorMessage -}}</div> 30 + {{- with .ErrorDetails }} 31 + <div class="break-words whitespace-pre-wrap font-mono">{{- . -}}</div> 25 32 {{- end }} 26 - > 27 - {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 28 - <span class="group-[.htmx-request]:hidden">Retry pipeline</span> 29 - </button> 30 - <div class="flex items-center gap-2"> 31 - <div id="workflow-error" class="text-red-500 dark:text-red-400"></div> 32 - <button 33 - class="btn group" 34 - hx-post="/{{ $.RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/workflow/{{ .Workflow }}/retry" 35 - hx-swap="none" 36 - hx-disabled-elt="this" 37 - {{ if not $status.IsFinish -}} 38 - disabled 39 - {{- end }} 40 - > 41 - {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 42 - <span class="group-[.htmx-request]:hidden">Retry workflow</span> 43 - </button> 44 - <button 45 - class="btn group" 46 - hx-post="/{{ $.RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/workflow/{{ .Workflow }}/cancel" 47 - hx-swap="none" 48 - hx-disabled-elt="this" 49 - {{ if $status.IsFinish -}} 50 - disabled 51 - {{- end }} 52 - > 53 - {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 54 - <span class="group-[.htmx-request]:hidden">Cancel</span> 55 - </button> 56 33 </div> 57 34 </div> 58 35 {{ end }} 59 - {{ with (index .Pipeline.Statuses .Workflow).Latest }} 60 - {{ if .Error }} 61 - <div class="mb-2 flex items-start gap-2 p-3 rounded border border-red-200 dark:border-red-900/60 bg-red-50/60 dark:bg-red-950/20 text-red-700 dark:text-red-400 text-sm"> 62 - {{ i "triangle-alert" "size-4 shrink-0 mt-0.5" }} 63 - <div class="flex-1 min-w-0"> 64 - <div class="break-words whitespace-pre-wrap">{{- .ErrorMessage -}}</div> 65 - {{- with .ErrorDetails }} 66 - <div class="font-mono break-words whitespace-pre-wrap">{{- . -}}</div> 67 - {{- end }} 68 - </div> 69 - </div> 70 - {{ end }} 71 - {{ end }} 72 - {{ block "logs" . }} {{ end }} 73 - </div> 74 - </section> 75 - {{ template "fragments/workflow-timers" }} 36 + {{ end }} 37 + {{ template "logs" . }} 76 38 {{ end }} 77 39 78 40 {{ define "sidebar" }} 79 41 {{ $active := .Workflow }} 80 42 81 - {{ $activeTab := "bg-white dark:bg-gray-700 drop-shadow-sm" }} 82 - {{ $inactiveTab := "bg-gray-100 dark:bg-gray-800" }} 83 - 84 43 {{ with .Pipeline }} 85 44 {{ $id := .Id }} 86 - <div class="sticky top-2 grid grid-cols-1 rounded border border-gray-200 dark:border-gray-700 divide-y divide-gray-200 dark:divide-gray-700"> 87 - {{ range $name, $all := .Statuses }} 88 - <a href="/{{ $.RepoInfo.FullName }}/pipelines/{{ $id }}/workflow/{{ $name }}" class="no-underline hover:no-underline hover:bg-gray-100/25 hover:dark:bg-gray-700/25"> 45 + {{ $pipeline := . }} 46 + <div class="sticky top-2 flex h-full min-h-[398px] w-full flex-col gap-2 lg:w-[240px]"> 47 + <div class="flex flex-col gap-2"> 48 + {{ range $name, $all := .Statuses }} 49 + {{ $lastStatus := $all.Latest }} 50 + {{ $status := $lastStatus.Status }} 51 + {{ $kind := $status.String }} 89 52 <div 90 - class="flex gap-2 items-center justify-between p-2 {{ if eq $name $active }} {{ $activeTab }} {{ else }} {{ $inactiveTab }} {{ end }}"> 91 - {{ $lastStatus := $all.Latest }} 92 - {{ $kind := $lastStatus.Status.String }} 93 - 94 - <div id="left" class="flex items-center gap-2 flex-1 min-w-0"> 95 - <div id="workflow-symbol-{{ normalizeForHtmlId $name }}" class="flex-shrink-0"> 96 - {{ template "repo/pipelines/fragments/workflowSymbol" $all }} 53 + class="flex flex-col gap-1.5 rounded px-3 py-2.5 transition-colors {{ if eq $name $active }}border border-transparent bg-gray-100 dark:bg-gray-700{{ else }}border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800{{ end }}"> 54 + <div class="flex h-5 items-center justify-between gap-[14px]"> 55 + <a href="/{{ $.RepoInfo.FullName }}/pipelines/{{ $id }}/workflow/{{ $name }}" class="flex min-w-0 items-center gap-0.5 no-underline hover:no-underline"> 56 + <span class="truncate text-sm font-medium leading-5 text-gray-900 dark:text-gray-100" title="{{ $name }}">{{ $name }}</span> 57 + {{ i "arrow-up-right" "size-4 shrink-0 text-gray-900 dark:text-gray-100" }} 58 + </a> 59 + <div class="shrink-0 text-sm leading-5 text-gray-500 dark:text-gray-400"> 60 + {{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }} 97 61 </div> 98 - <span class="truncate" title="{{ $name }}"> 99 - {{ $name }} 100 - </span> 101 62 </div> 102 - <div id="right" class="flex items-center gap-2 flex-shrink-0"> 103 - <span class="font-bold">{{ $kind }}</span> 104 - {{ if .TimeTaken }} 105 - {{ template "repo/fragments/duration" .TimeTaken }} 106 - {{ else }} 107 - {{ template "repo/fragments/shortTimeAgo" $lastStatus.Created }} 108 - {{ end }} 63 + <div class="flex h-8 items-center justify-between gap-2"> 64 + <div class="flex min-w-0 items-center gap-1"> 65 + <div id="workflow-symbol-{{ normalizeForHtmlId $name }}" class="flex-shrink-0"> 66 + {{ template "repo/pipelines/fragments/workflowSymbol" $all }} 67 + </div> 68 + <span class="truncate text-sm font-semibold leading-5 text-gray-900 dark:text-gray-100">{{ $kind }}</span> 69 + </div> 70 + {{ template "workflowActions" (dict "Root" $ "Pipeline" $pipeline "Name" $name "Kind" $kind "Status" $status) }} 109 71 </div> 110 72 </div> 111 - </a> 73 + {{ end }} 74 + </div> 75 + {{ template "sidebarActions" $ }} 76 + </div> 77 + {{ end }} 78 + {{ end }} 79 + 80 + {{ define "workflowActions" }} 81 + {{ $root := .Root }} 82 + {{ $status := .Status }} 83 + {{ $kind := .Kind }} 84 + {{ if and $root.RepoInfo.Roles.IsPushAllowed (ne $kind "success") }} 85 + <div class="flex shrink-0 items-center gap-0.5"> 86 + <button 87 + class="group inline-flex h-8 w-[30px] min-h-8 items-center justify-center rounded px-2 py-1.5 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-700" 88 + title="Retry workflow" 89 + hx-post="/{{ $root.RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/workflow/{{ .Name }}/retry" 90 + hx-swap="none" 91 + hx-disabled-elt="this" 92 + > 93 + {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 94 + {{ i "rotate-ccw" "size-4 group-[.htmx-request]:hidden" }} 95 + </button> 96 + {{ if not $status.IsFinish }} 97 + <button 98 + class="group inline-flex h-8 w-[30px] min-h-8 items-center justify-center rounded px-2 py-1.5 text-gray-900 dark:text-gray-100 hover:bg-gray-200 dark:hover:bg-gray-700" 99 + title="Cancel workflow" 100 + hx-post="/{{ $root.RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/workflow/{{ .Name }}/cancel" 101 + hx-swap="none" 102 + hx-disabled-elt="this" 103 + > 104 + {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 105 + {{ i "x" "size-4 group-[.htmx-request]:hidden" }} 106 + </button> 112 107 {{ end }} 113 108 </div> 114 109 {{ end }} 115 110 {{ end }} 116 111 112 + {{ define "sidebarActions" }} 113 + <div class="mt-auto flex w-full flex-col gap-2"> 114 + {{ with .SSHLogCommand }} 115 + <button type="button" class="btn flex h-9 min-h-9 w-full items-center justify-center gap-1.5 px-2 py-2 text-sm leading-5" onclick="navigator.clipboard.writeText({{ printf "%q" . }})" title="{{ . }}"> 116 + {{ i "copy" "size-4" }} Watch logs via SSH 117 + </button> 118 + {{ end }} 119 + {{ if .RepoInfo.Roles.IsPushAllowed }} 120 + <div class="flex w-full gap-2"> 121 + <div id="workflow-error" class="empty:hidden text-red-500 dark:text-red-400"></div> 122 + <button 123 + class="btn group flex h-9 min-h-9 flex-1 items-center justify-center gap-1.5 px-2 py-2 text-sm leading-5" 124 + hx-post="/{{ .RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/retry" 125 + hx-swap="none" 126 + hx-disabled-elt="this" 127 + > 128 + {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 129 + {{ i "rotate-ccw" "size-4 group-[.htmx-request]:hidden" }} 130 + <span>Retry all</span> 131 + </button> 132 + {{ if .Pipeline.InProgress }} 133 + <button 134 + class="btn group flex h-9 min-h-9 flex-1 items-center justify-center gap-1.5 px-2 py-2 text-sm leading-5" 135 + hx-post="/{{ .RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/cancel" 136 + hx-swap="none" 137 + hx-disabled-elt="this" 138 + > 139 + {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 140 + {{ i "circle-stop" "size-4 group-[.htmx-request]:hidden" }} 141 + <span>Cancel all</span> 142 + </button> 143 + {{ end }} 144 + </div> 145 + {{ end }} 146 + </div> 147 + {{ end }} 148 + 117 149 {{ define "logs" }} 118 150 <div id="log-stream" 119 151 class="text-sm" 120 152 hx-ext="ws" 121 153 ws-connect="/{{ $.RepoInfo.FullName }}/pipelines/{{ .Pipeline.Id }}/workflow/{{ .Workflow }}/logs"> 122 - <div id="lines" class="flex flex-col gap-2"> 123 - <div class="text-base text-gray-500 flex items-center justify-center italic p-12 only:flex hidden border border-gray-200 dark:border-gray-700 rounded"> 154 + <div id="lines" class="flex flex-col gap-3"> 155 + <div class="hidden items-center justify-center rounded border border-gray-200 p-12 text-base italic text-gray-500 only:flex dark:border-gray-700"> 124 156 <span class="flex items-center gap-2"> 125 157 {{ i "triangle-alert" "size-4" }} No logs for this workflow 126 158 </span>
+46 -21
appview/pipelines/pipelines.go
··· 5 5 "context" 6 6 "fmt" 7 7 "log/slog" 8 + "net" 8 9 "net/http" 9 10 "sync" 10 11 "time" ··· 47 48 r.Get("/{pipeline}/workflow/{workflow}/logs", p.Logs) 48 49 r.Group(func(r chi.Router) { 49 50 r.Use(mw.RepoPermissionMiddleware("repo:push")) 51 + r.Post("/{pipeline}/cancel", p.CancelPipeline) 50 52 r.Post("/{pipeline}/workflow/{workflow}/cancel", p.CancelWorkflow) 51 53 r.Post("/{pipeline}/retry", p.RetryPipeline) 52 54 r.Post("/{pipeline}/workflow/{workflow}/retry", p.RetryWorkflow) ··· 220 222 } 221 223 222 224 p.pages.Workflow(w, pages.WorkflowParams{ 223 - BaseParams: pages.BaseParamsFromContext(r.Context()), 224 - RepoInfo: p.repoResolver.GetRepoInfo(r, user), 225 - Pipeline: types.Pipeline{CiPipeline: out}, 226 - Workflow: workflowName, 225 + BaseParams: pages.BaseParamsFromContext(r.Context()), 226 + RepoInfo: p.repoResolver.GetRepoInfo(r, user), 227 + Pipeline: types.Pipeline{CiPipeline: out}, 228 + Workflow: workflowName, 229 + SSHLogCommand: p.sshLogCommand(f.RepoDid, out.Commit), 227 230 }) 231 + } 232 + 233 + func (p *Pipelines) sshLogCommand(repoDid, sha string) string { 234 + if p.config == nil || !p.config.SSH.Enabled || sha == "" { 235 + return "" 236 + } 237 + _, port, err := net.SplitHostPort(p.config.SSH.ListenAddr) 238 + if err != nil || port == "" { 239 + return "" 240 + } 241 + return fmt.Sprintf("ssh -t -p %s %s %s %s", port, p.config.Core.AppviewHost, repoDid, sha) 228 242 } 229 243 230 244 var upgrader = websocket.Upgrader{ ··· 476 490 } 477 491 } 478 492 493 + func (p *Pipelines) CancelPipeline(w http.ResponseWriter, r *http.Request) { 494 + p.cancel(w, r, nil) 495 + } 496 + 479 497 func (p *Pipelines) CancelWorkflow(w http.ResponseWriter, r *http.Request) { 480 - l := p.logger.With("handler", "CancelWorkflow") 498 + workflowName := chi.URLParam(r, "workflow") 499 + if workflowName == "" { 500 + p.logger.With("handler", "CancelWorkflow").Debug("empty workflow name") 501 + p.pages.Error404(w) 502 + return 503 + } 504 + p.cancel(w, r, []string{workflowName}) 505 + } 506 + 507 + func (p *Pipelines) cancel(w http.ResponseWriter, r *http.Request, workflows []string) { 508 + l := p.logger.With("handler", "cancel", "workflows", workflows) 481 509 errorId := "workflow-error" 510 + target := "pipeline" 511 + if len(workflows) == 1 { 512 + target = "workflow" 513 + } 514 + fail := "Failed to cancel " + target 482 515 483 516 f, err := p.repoResolver.Resolve(r) 484 517 if err != nil { 485 518 l.Error("failed to get repo and knot", "err", err) 486 - p.pages.Notice(w, errorId, "Failed to cancel workflow") 519 + p.pages.Notice(w, errorId, fail) 487 520 return 488 521 } 489 522 l = l.With("repo", f.RepoDid) 490 523 491 524 if f.Spindle == "" { 492 525 l.Debug("spindle is empty") 493 - p.pages.Notice(w, errorId, "Failed to cancel workflow") 526 + p.pages.Notice(w, errorId, fail) 494 527 return 495 528 } 496 529 ··· 500 533 p.pages.Error404(w) 501 534 return 502 535 } 503 - 504 - workflowName := chi.URLParam(r, "workflow") 505 - if workflowName == "" { 506 - l.Debug("empty workflow name") 507 - p.pages.Error404(w) 508 - return 509 - } 510 - 511 - l = l.With("pipeline", pipelineId, "workflow", workflowName) 536 + l = l.With("pipeline", pipelineId) 512 537 513 538 spindleClient, err := p.oauth.SpindleServiceClient(r, f.Spindle, tangled.CiCancelPipelineNSID) 514 539 if err != nil { 515 540 l.Error("failed to prepare spindle client", "err", err) 516 - p.pages.Notice(w, errorId, "Failed to cancel workflow") 541 + p.pages.Notice(w, errorId, fail) 517 542 return 518 543 } 519 544 ··· 523 548 &tangled.CiCancelPipeline_Input{ 524 549 Repo: f.RepoDid, 525 550 Pipeline: pipelineId.String(), 526 - Workflows: []string{workflowName}, 551 + Workflows: workflows, 527 552 }, 528 553 ); err != nil { 529 - l.Error("failed to cancel workflow", "err", err) 530 - p.pages.Notice(w, errorId, "Failed to cancel workflow") 554 + l.Error("failed to cancel pipeline", "err", err) 555 + p.pages.Notice(w, errorId, fail) 531 556 return 532 557 } 533 - l.Debug("canceled workflow") 558 + l.Debug("canceled pipeline") 534 559 } 535 560 536 561 // RetryPipeline retries all workflows in a pipeline
+3
docker-compose.yml
··· 339 339 TANGLED_REDIS_ADDR: redis:6379 340 340 TANGLED_KNOTMIRROR_URL: https://mirror.tngl.boltless.dev 341 341 TANGLED_CODESEARCH_ZOEKT_URL: https://zoekt.tngl.boltless.dev 342 + TANGLED_SSH_ENABLED: "true" 343 + TANGLED_SSH_LISTEN_ADDR: "0.0.0.0:3333" 342 344 ports: 343 345 - "3000:3000" 346 + - "3333:3333" 344 347 volumes: 345 348 - .:/src:cached 346 349 - go-cache:/go/cache
+2
types/pipeline.go
··· 103 103 return t 104 104 } 105 105 106 + 107 + 106 108 type Trigger struct { 107 109 *tangled.CiPipeline_Trigger 108 110 }