174 lines
5.1 KiB
Go
174 lines
5.1 KiB
Go
// Copyright 2025 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package actions
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
actions_model "code.gitea.io/gitea/models/actions"
|
|
"code.gitea.io/gitea/models/perm"
|
|
access_model "code.gitea.io/gitea/models/perm/access"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/actions"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/log"
|
|
"code.gitea.io/gitea/modules/reqctx"
|
|
api "code.gitea.io/gitea/modules/structs"
|
|
webhook_module "code.gitea.io/gitea/modules/webhook"
|
|
"code.gitea.io/gitea/services/convert"
|
|
notify_service "code.gitea.io/gitea/services/notify"
|
|
|
|
"github.com/nektos/act/pkg/jobparser"
|
|
)
|
|
|
|
// DebugWorkflow starts a debug run for a workflow
|
|
func DebugWorkflow(ctx reqctx.RequestContext, doer *user_model.User, repo *repo_model.Repository,
|
|
gitRepo *git.Repository, debugSessionID int64, workflowContent string, ref string,
|
|
event string, inputs map[string]string, env map[string]string) (int64, error) {
|
|
|
|
if workflowContent == "" {
|
|
return 0, fmt.Errorf("workflow content is empty")
|
|
}
|
|
|
|
if ref == "" {
|
|
ref = repo.DefaultBranch
|
|
}
|
|
|
|
if event == "" {
|
|
event = "push"
|
|
}
|
|
|
|
// Get target commit from ref
|
|
refName := git.RefName(ref)
|
|
var runTargetCommit *git.Commit
|
|
var err error
|
|
|
|
if refName.IsTag() {
|
|
runTargetCommit, err = gitRepo.GetTagCommit(refName.TagName())
|
|
} else if refName.IsBranch() {
|
|
runTargetCommit, err = gitRepo.GetBranchCommit(refName.BranchName())
|
|
} else {
|
|
refName = git.RefNameFromBranch(ref)
|
|
runTargetCommit, err = gitRepo.GetBranchCommit(ref)
|
|
}
|
|
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to get commit: %w", err)
|
|
}
|
|
|
|
// Parse workflow from content
|
|
workflows, err := jobparser.Parse([]byte(workflowContent))
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to parse workflow: %w", err)
|
|
}
|
|
|
|
if len(workflows) == 0 {
|
|
return 0, fmt.Errorf("no workflows found in content")
|
|
}
|
|
|
|
// Create action run
|
|
run := &actions_model.ActionRun{
|
|
Title: fmt.Sprintf("Debug: %s", workflows[0].Name),
|
|
RepoID: repo.ID,
|
|
Repo: repo,
|
|
OwnerID: repo.OwnerID,
|
|
WorkflowID: fmt.Sprintf("debug_%d", debugSessionID),
|
|
TriggerUserID: doer.ID,
|
|
TriggerUser: doer,
|
|
Ref: string(refName),
|
|
CommitSHA: runTargetCommit.ID.String(),
|
|
IsForkPullRequest: false,
|
|
Event: webhook_module.HookEventType(event),
|
|
TriggerEvent: event,
|
|
Status: actions_model.StatusWaiting,
|
|
}
|
|
|
|
// Build event payload
|
|
eventPayload := map[string]interface{}{
|
|
"inputs": inputs,
|
|
"ref": ref,
|
|
}
|
|
|
|
// Convert to API payload
|
|
workflowDispatchPayload := &api.WorkflowDispatchPayload{
|
|
Workflow: fmt.Sprintf("debug_%d", debugSessionID),
|
|
Ref: ref,
|
|
Repository: convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeNone}),
|
|
Inputs: eventPayload,
|
|
Sender: convert.ToUserWithAccessMode(ctx, doer, perm.AccessModeNone),
|
|
}
|
|
|
|
var eventPayloadBytes []byte
|
|
if eventPayloadBytes, err = workflowDispatchPayload.JSONPayload(); err != nil {
|
|
return 0, fmt.Errorf("JSONPayload: %w", err)
|
|
}
|
|
|
|
run.EventPayload = string(eventPayloadBytes)
|
|
|
|
// Insert the action run and its associated jobs into the database
|
|
if err := actions_model.InsertRun(ctx, run, workflows); err != nil {
|
|
return 0, fmt.Errorf("InsertRun: %w", err)
|
|
}
|
|
|
|
// Update debug session with run ID
|
|
debugSession, err := actions_model.GetDebugSession(ctx, debugSessionID)
|
|
if err == nil && debugSession != nil {
|
|
debugSession.RunID = run.ID
|
|
debugSession.Status = actions_model.DebugSessionStatusRunning
|
|
_ = actions_model.UpdateDebugSession(ctx, debugSession)
|
|
}
|
|
|
|
// Trigger notification to start the run
|
|
notify_service.WorkflowRunStatusUpdate(ctx, run.Repo, run.TriggerUser, run)
|
|
|
|
log.Info("Debug workflow started: run_id=%d, debug_session_id=%d", run.ID, debugSessionID)
|
|
|
|
return run.Index, nil
|
|
}
|
|
|
|
// GetDebugWorkflowStatus returns the status of a debug workflow run
|
|
func GetDebugWorkflowStatus(ctx reqctx.RequestContext, debugSessionID int64) (*actions_model.ActionRun, error) {
|
|
debugSession, err := actions_model.GetDebugSession(ctx, debugSessionID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if debugSession.RunID == 0 {
|
|
return nil, nil // Not run yet
|
|
}
|
|
|
|
return actions_model.GetRunByRepoAndID(ctx, debugSession.RepoID, debugSession.RunID)
|
|
}
|
|
|
|
// GetDebugSessionWorkflowContent retrieves the workflow content from repository for a debug session
|
|
func GetDebugSessionWorkflowContent(ctx reqctx.RequestContext, repo *repo_model.Repository, gitRepo *git.Repository,
|
|
workflowID string) (string, error) {
|
|
|
|
// Get default branch commit
|
|
commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// List workflows
|
|
_, entries, err := actions.ListWorkflows(commit)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// Find the workflow file
|
|
for _, entry := range entries {
|
|
if entry.Name() == workflowID {
|
|
content, err := actions.GetContentFromEntry(entry)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(content), nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("workflow %q not found", workflowID)
|
|
}
|