github.com/nektos/act@v0.2.83/pkg/model/github_context.go (about)

     1  package model
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/nektos/act/pkg/common"
     9  	"github.com/nektos/act/pkg/common/git"
    10  )
    11  
    12  type GithubContext struct {
    13  	Event            map[string]interface{} `json:"event"`
    14  	EventPath        string                 `json:"event_path"`
    15  	Workflow         string                 `json:"workflow"`
    16  	RunAttempt       string                 `json:"run_attempt"`
    17  	RunID            string                 `json:"run_id"`
    18  	RunNumber        string                 `json:"run_number"`
    19  	Actor            string                 `json:"actor"`
    20  	Repository       string                 `json:"repository"`
    21  	EventName        string                 `json:"event_name"`
    22  	Sha              string                 `json:"sha"`
    23  	Ref              string                 `json:"ref"`
    24  	RefName          string                 `json:"ref_name"`
    25  	RefType          string                 `json:"ref_type"`
    26  	HeadRef          string                 `json:"head_ref"`
    27  	BaseRef          string                 `json:"base_ref"`
    28  	Token            string                 `json:"token"`
    29  	Workspace        string                 `json:"workspace"`
    30  	Action           string                 `json:"action"`
    31  	ActionPath       string                 `json:"action_path"`
    32  	ActionRef        string                 `json:"action_ref"`
    33  	ActionRepository string                 `json:"action_repository"`
    34  	Job              string                 `json:"job"`
    35  	JobName          string                 `json:"job_name"`
    36  	RepositoryOwner  string                 `json:"repository_owner"`
    37  	RetentionDays    string                 `json:"retention_days"`
    38  	RunnerPerflog    string                 `json:"runner_perflog"`
    39  	RunnerTrackingID string                 `json:"runner_tracking_id"`
    40  	ServerURL        string                 `json:"server_url"`
    41  	APIURL           string                 `json:"api_url"`
    42  	GraphQLURL       string                 `json:"graphql_url"`
    43  }
    44  
    45  func asString(v interface{}) string {
    46  	if v == nil {
    47  		return ""
    48  	} else if s, ok := v.(string); ok {
    49  		return s
    50  	}
    51  	return ""
    52  }
    53  
    54  func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{}) {
    55  	var ok bool
    56  
    57  	if len(ks) == 0 { // degenerate input
    58  		return nil
    59  	}
    60  	if rval, ok = m[ks[0]]; !ok {
    61  		return nil
    62  	} else if len(ks) == 1 { // we've reached the final key
    63  		return rval
    64  	} else if m, ok = rval.(map[string]interface{}); !ok {
    65  		return nil
    66  	}
    67  	// 1+ more keys
    68  	return nestedMapLookup(m, ks[1:]...)
    69  }
    70  
    71  func withDefaultBranch(ctx context.Context, b string, event map[string]interface{}) map[string]interface{} {
    72  	repoI, ok := event["repository"]
    73  	if !ok {
    74  		repoI = make(map[string]interface{})
    75  	}
    76  
    77  	repo, ok := repoI.(map[string]interface{})
    78  	if !ok {
    79  		common.Logger(ctx).Warnf("unable to set default branch to %v", b)
    80  		return event
    81  	}
    82  
    83  	// if the branch is already there return with no changes
    84  	if _, ok = repo["default_branch"]; ok {
    85  		return event
    86  	}
    87  
    88  	repo["default_branch"] = b
    89  	event["repository"] = repo
    90  
    91  	return event
    92  }
    93  
    94  var findGitRef = git.FindGitRef
    95  var findGitRevision = git.FindGitRevision
    96  
    97  func (ghc *GithubContext) SetRef(ctx context.Context, defaultBranch string, repoPath string) {
    98  	logger := common.Logger(ctx)
    99  
   100  	// https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows
   101  	// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads
   102  	switch ghc.EventName {
   103  	case "pull_request_target":
   104  		ghc.Ref = fmt.Sprintf("refs/heads/%s", ghc.BaseRef)
   105  	case "pull_request", "pull_request_review", "pull_request_review_comment":
   106  		ghc.Ref = fmt.Sprintf("refs/pull/%.0f/merge", ghc.Event["number"])
   107  	case "deployment", "deployment_status":
   108  		ghc.Ref = asString(nestedMapLookup(ghc.Event, "deployment", "ref"))
   109  	case "release":
   110  		ghc.Ref = fmt.Sprintf("refs/tags/%s", asString(nestedMapLookup(ghc.Event, "release", "tag_name")))
   111  	case "push", "create", "workflow_dispatch":
   112  		ghc.Ref = asString(ghc.Event["ref"])
   113  	default:
   114  		defaultBranch := asString(nestedMapLookup(ghc.Event, "repository", "default_branch"))
   115  		if defaultBranch != "" {
   116  			ghc.Ref = fmt.Sprintf("refs/heads/%s", defaultBranch)
   117  		}
   118  	}
   119  
   120  	if ghc.Ref == "" {
   121  		ref, err := findGitRef(ctx, repoPath)
   122  		if err != nil {
   123  			logger.Warningf("unable to get git ref: %v", err)
   124  		} else {
   125  			logger.Debugf("using github ref: %s", ref)
   126  			ghc.Ref = ref
   127  		}
   128  
   129  		// set the branch in the event data
   130  		if defaultBranch != "" {
   131  			ghc.Event = withDefaultBranch(ctx, defaultBranch, ghc.Event)
   132  		} else {
   133  			ghc.Event = withDefaultBranch(ctx, "master", ghc.Event)
   134  		}
   135  
   136  		if ghc.Ref == "" {
   137  			ghc.Ref = fmt.Sprintf("refs/heads/%s", asString(nestedMapLookup(ghc.Event, "repository", "default_branch")))
   138  		}
   139  	}
   140  }
   141  
   142  func (ghc *GithubContext) SetSha(ctx context.Context, repoPath string) {
   143  	logger := common.Logger(ctx)
   144  
   145  	// https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows
   146  	// https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads
   147  	switch ghc.EventName {
   148  	case "pull_request_target":
   149  		ghc.Sha = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "sha"))
   150  	case "deployment", "deployment_status":
   151  		ghc.Sha = asString(nestedMapLookup(ghc.Event, "deployment", "sha"))
   152  	case "push", "create", "workflow_dispatch":
   153  		if deleted, ok := ghc.Event["deleted"].(bool); ok && !deleted {
   154  			ghc.Sha = asString(ghc.Event["after"])
   155  		}
   156  	}
   157  
   158  	if ghc.Sha == "" {
   159  		_, sha, err := findGitRevision(ctx, repoPath)
   160  		if err != nil {
   161  			logger.Warningf("unable to get git revision: %v", err)
   162  		} else {
   163  			ghc.Sha = sha
   164  		}
   165  	}
   166  }
   167  
   168  func (ghc *GithubContext) SetRepositoryAndOwner(ctx context.Context, githubInstance string, remoteName string, repoPath string) {
   169  	if ghc.Repository == "" {
   170  		repo, err := git.FindGithubRepo(ctx, repoPath, githubInstance, remoteName)
   171  		if err != nil {
   172  			common.Logger(ctx).Debugf("unable to get git repo (githubInstance: %v; remoteName: %v, repoPath: %v): %v", githubInstance, remoteName, repoPath, err)
   173  			// nektos/act is used as a default action, so why not a repo?
   174  			ghc.Repository = "nektos/act"
   175  			ghc.RepositoryOwner = strings.Split(ghc.Repository, "/")[0]
   176  			return
   177  		}
   178  		ghc.Repository = repo
   179  	}
   180  	ghc.RepositoryOwner = strings.Split(ghc.Repository, "/")[0]
   181  }
   182  
   183  func (ghc *GithubContext) SetRefTypeAndName() {
   184  	var refType, refName string
   185  
   186  	// https://docs.github.com/en/actions/learn-github-actions/environment-variables
   187  	if strings.HasPrefix(ghc.Ref, "refs/tags/") {
   188  		refType = "tag"
   189  		refName = ghc.Ref[len("refs/tags/"):]
   190  	} else if strings.HasPrefix(ghc.Ref, "refs/heads/") {
   191  		refType = "branch"
   192  		refName = ghc.Ref[len("refs/heads/"):]
   193  	} else if strings.HasPrefix(ghc.Ref, "refs/pull/") {
   194  		refType = ""
   195  		refName = ghc.Ref[len("refs/pull/"):]
   196  	}
   197  
   198  	if ghc.RefType == "" {
   199  		ghc.RefType = refType
   200  	}
   201  
   202  	if ghc.RefName == "" {
   203  		ghc.RefName = refName
   204  	}
   205  }
   206  
   207  func (ghc *GithubContext) SetBaseAndHeadRef() {
   208  	if ghc.EventName == "pull_request" || ghc.EventName == "pull_request_target" {
   209  		if ghc.BaseRef == "" {
   210  			ghc.BaseRef = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "ref"))
   211  		}
   212  
   213  		if ghc.HeadRef == "" {
   214  			ghc.HeadRef = asString(nestedMapLookup(ghc.Event, "pull_request", "head", "ref"))
   215  		}
   216  	}
   217  }