+
{{ i "triangle-alert" "size-4" }} No logs for this workflow
diff --git a/appview/pipelines/pipelines.go b/appview/pipelines/pipelines.go
--- a/appview/pipelines/pipelines.go
+++ b/appview/pipelines/pipelines.go
@@ -5,6 +5,7 @@ "bytes"
"context"
"fmt"
"log/slog"
+ "net"
"net/http"
"sync"
"time"
@@ -46,6 +47,7 @@ r.Get("/{pipeline}/workflow/{workflow}", p.Workflow)
r.Get("/{pipeline}/workflow/{workflow}/logs", p.Logs)
r.Group(func(r chi.Router) {
r.Use(mw.RepoPermissionMiddleware("repo:push"))
+ r.Post("/{pipeline}/cancel", p.CancelPipeline)
r.Post("/{pipeline}/workflow/{workflow}/cancel", p.CancelWorkflow)
r.Post("/{pipeline}/retry", p.RetryPipeline)
r.Post("/{pipeline}/workflow/{workflow}/retry", p.RetryWorkflow)
@@ -223,11 +225,23 @@ return
}
p.pages.Workflow(w, pages.WorkflowParams{
- BaseParams: pages.BaseParamsFromContext(r.Context()),
- RepoInfo: p.repoResolver.GetRepoInfo(r, user),
- Pipeline: types.Pipeline{CiPipeline: out},
- Workflow: workflowName,
+ BaseParams: pages.BaseParamsFromContext(r.Context()),
+ RepoInfo: p.repoResolver.GetRepoInfo(r, user),
+ Pipeline: types.Pipeline{CiPipeline: out},
+ Workflow: workflowName,
+ SSHLogCommand: p.sshLogCommand(f.RepoDid, out.Commit),
})
+}
+
+func (p *Pipelines) sshLogCommand(repoDid, sha string) string {
+ if p.config == nil || !p.config.SSH.Enabled || sha == "" {
+ return ""
+ }
+ _, port, err := net.SplitHostPort(p.config.SSH.ListenAddr)
+ if err != nil || port == "" {
+ return ""
+ }
+ return fmt.Sprintf("ssh -t -p %s %s %s %s", port, p.config.Core.AppviewHost, repoDid, sha)
}
var upgrader = websocket.Upgrader{
@@ -479,21 +493,40 @@ }
}
}
+func (p *Pipelines) CancelPipeline(w http.ResponseWriter, r *http.Request) {
+ p.cancel(w, r, nil)
+}
+
func (p *Pipelines) CancelWorkflow(w http.ResponseWriter, r *http.Request) {
- l := p.logger.With("handler", "CancelWorkflow")
+ workflowName := chi.URLParam(r, "workflow")
+ if workflowName == "" {
+ p.logger.With("handler", "CancelWorkflow").Debug("empty workflow name")
+ p.pages.Error404(w)
+ return
+ }
+ p.cancel(w, r, []string{workflowName})
+}
+
+func (p *Pipelines) cancel(w http.ResponseWriter, r *http.Request, workflows []string) {
+ l := p.logger.With("handler", "cancel", "workflows", workflows)
errorId := "workflow-error"
+ target := "pipeline"
+ if len(workflows) == 1 {
+ target = "workflow"
+ }
+ fail := "Failed to cancel " + target
f, err := p.repoResolver.Resolve(r)
if err != nil {
l.Error("failed to get repo and knot", "err", err)
- p.pages.Notice(w, errorId, "Failed to cancel workflow")
+ p.pages.Notice(w, errorId, fail)
return
}
l = l.With("repo", f.RepoDid)
if f.Spindle == "" {
l.Debug("spindle is empty")
- p.pages.Notice(w, errorId, "Failed to cancel workflow")
+ p.pages.Notice(w, errorId, fail)
return
}
@@ -503,20 +536,12 @@ l.Debug("invalid pipeline id", "id", pipelineId)
p.pages.Error404(w)
return
}
-
- workflowName := chi.URLParam(r, "workflow")
- if workflowName == "" {
- l.Debug("empty workflow name")
- p.pages.Error404(w)
- return
- }
-
- l = l.With("pipeline", pipelineId, "workflow", workflowName)
+ l = l.With("pipeline", pipelineId)
spindleClient, err := p.oauth.SpindleServiceClient(r, f.Spindle, tangled.CiCancelPipelineNSID)
if err != nil {
l.Error("failed to prepare spindle client", "err", err)
- p.pages.Notice(w, errorId, "Failed to cancel workflow")
+ p.pages.Notice(w, errorId, fail)
return
}
@@ -526,14 +551,14 @@ spindleClient,
&tangled.CiCancelPipeline_Input{
Repo: f.RepoDid,
Pipeline: pipelineId.String(),
- Workflows: []string{workflowName},
+ Workflows: workflows,
},
); err != nil {
- l.Error("failed to cancel workflow", "err", err)
- p.pages.Notice(w, errorId, "Failed to cancel workflow")
+ l.Error("failed to cancel pipeline", "err", err)
+ p.pages.Notice(w, errorId, fail)
return
}
- l.Debug("canceled workflow")
+ l.Debug("canceled pipeline")
}
// RetryPipeline retries all workflows in a pipeline
diff --git a/docker-compose.yml b/docker-compose.yml
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -340,8 +340,11 @@ TANGLED_JETSTREAM_ENDPOINT: wss://jetstream.tngl.boltless.dev/subscribe
TANGLED_REDIS_ADDR: redis:6379
TANGLED_KNOTMIRROR_URL: https://mirror.tngl.boltless.dev
TANGLED_CODESEARCH_ZOEKT_URL: https://zoekt.tngl.boltless.dev
+ TANGLED_SSH_ENABLED: "true"
+ TANGLED_SSH_LISTEN_ADDR: "0.0.0.0:3333"
ports:
- "3000:3000"
+ - "3333:3333"
volumes:
- .:/src:cached
- go-cache:/go/cache
diff --git a/types/pipeline.go b/types/pipeline.go
--- a/types/pipeline.go
+++ b/types/pipeline.go
@@ -103,6 +103,8 @@ }
return t
}
+
+
type Trigger struct {
*tangled.CiPipeline_Trigger
}