sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/github/helpers.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package github
    18  
    19  import (
    20  	"fmt"
    21  	"net/http"
    22  	"regexp"
    23  	"strconv"
    24  	"strings"
    25  )
    26  
    27  // SecurityForkNameRE is a regexp matching repos that are temporary security forks.
    28  // https://help.github.com/en/github/managing-security-vulnerabilities/collaborating-in-a-temporary-private-fork-to-resolve-a-security-vulnerability
    29  var SecurityForkNameRE = regexp.MustCompile(`^[\w-]+-ghsa-[\w-]+$`)
    30  
    31  // ImageSizeLimit is the maximum image size GitHub allows in bytes (5MB).
    32  const ImageSizeLimit = 5242880
    33  
    34  // HasLabel checks if label is in the label set "issueLabels".
    35  func HasLabel(label string, issueLabels []Label) bool {
    36  	for _, l := range issueLabels {
    37  		if strings.EqualFold(l.Name, label) {
    38  			return true
    39  		}
    40  	}
    41  	return false
    42  }
    43  
    44  // HasLabels checks if all labels are in the github.label set "issueLabels".
    45  func HasLabels(labels []string, issueLabels []Label) bool {
    46  	for _, label := range labels {
    47  		if !HasLabel(label, issueLabels) {
    48  			return false
    49  		}
    50  	}
    51  	return true
    52  }
    53  
    54  // ImageTooBig checks if image is bigger than github limits.
    55  func ImageTooBig(url string) (bool, error) {
    56  	// try to get the image size from Content-Length header
    57  	resp, err := http.Head(url)
    58  	if err != nil {
    59  		return true, fmt.Errorf("HEAD error: %w", err)
    60  	}
    61  	defer resp.Body.Close()
    62  	if sc := resp.StatusCode; sc != http.StatusOK {
    63  		return true, fmt.Errorf("failing %d response", sc)
    64  	}
    65  	size, _ := strconv.Atoi(resp.Header.Get("Content-Length"))
    66  	if size > ImageSizeLimit {
    67  		return true, nil
    68  	}
    69  	return false, nil
    70  }
    71  
    72  // LevelFromPermissions adapts a repo permissions struct to the
    73  // appropriate permission level used elsewhere.
    74  func LevelFromPermissions(permissions RepoPermissions) RepoPermissionLevel {
    75  	if permissions.Admin {
    76  		return Admin
    77  	} else if permissions.Maintain {
    78  		return Maintain
    79  	} else if permissions.Push {
    80  		return Write
    81  	} else if permissions.Triage {
    82  		return Triage
    83  	} else if permissions.Pull {
    84  		return Read
    85  	} else {
    86  		return None
    87  	}
    88  }
    89  
    90  // PermissionsFromTeamPermissions
    91  func PermissionsFromTeamPermission(permission TeamPermission) RepoPermissions {
    92  	switch permission {
    93  	case RepoPull:
    94  		return RepoPermissions{Pull: true}
    95  	case RepoTriage:
    96  		return RepoPermissions{Pull: true, Triage: true}
    97  	case RepoPush:
    98  		return RepoPermissions{Pull: true, Triage: true, Push: true}
    99  	case RepoMaintain:
   100  		return RepoPermissions{Pull: true, Triage: true, Push: true, Maintain: true}
   101  	case RepoAdmin:
   102  		return RepoPermissions{Pull: true, Triage: true, Push: true, Maintain: true, Admin: true}
   103  	default:
   104  		// Should never happen unless the type gets new value
   105  		return RepoPermissions{}
   106  	}
   107  }
   108  
   109  // CommentLikeEventTypes are various event types that can be coerced into
   110  // a GenericCommentEvent.
   111  type CommentLikeEventTypes interface {
   112  	IssueEvent | IssueCommentEvent | PullRequestEvent | ReviewEvent | ReviewCommentEvent
   113  }
   114  
   115  // GeneralizeComment takes an event that can be coerced into a GenericCommentEvent
   116  // and returns a populated GenericCommentEvent.
   117  func GeneralizeComment[E CommentLikeEventTypes](event E) (*GenericCommentEvent, error) {
   118  	switch t := any(event).(type) {
   119  
   120  	case IssueEvent:
   121  		action := GeneralizeCommentAction(string(t.Action))
   122  		if action == "" {
   123  			return nil, fmt.Errorf("failed to determine events action [%v]", string(t.Action))
   124  		}
   125  
   126  		return &GenericCommentEvent{
   127  			ID:           t.Issue.ID,
   128  			NodeID:       t.Issue.NodeID,
   129  			GUID:         t.GUID,
   130  			IsPR:         t.Issue.IsPullRequest(),
   131  			Action:       action,
   132  			Body:         t.Issue.Body,
   133  			HTMLURL:      t.Issue.HTMLURL,
   134  			Number:       t.Issue.Number,
   135  			Repo:         t.Repo,
   136  			User:         t.Issue.User,
   137  			IssueAuthor:  t.Issue.User,
   138  			Assignees:    t.Issue.Assignees,
   139  			IssueState:   t.Issue.State,
   140  			IssueTitle:   t.Issue.Title,
   141  			IssueBody:    t.Issue.Body,
   142  			IssueHTMLURL: t.Issue.HTMLURL,
   143  		}, nil
   144  	case IssueCommentEvent:
   145  		action := GeneralizeCommentAction(string(t.Action))
   146  		if action == "" {
   147  			return nil, fmt.Errorf("failed to determine events action [%v]", string(t.Action))
   148  		}
   149  
   150  		return &GenericCommentEvent{
   151  			ID:           t.Issue.ID,
   152  			NodeID:       t.Issue.NodeID,
   153  			CommentID:    &t.Comment.ID,
   154  			GUID:         t.GUID,
   155  			IsPR:         t.Issue.IsPullRequest(),
   156  			Action:       action,
   157  			Body:         t.Comment.Body,
   158  			HTMLURL:      t.Comment.HTMLURL,
   159  			Number:       t.Issue.Number,
   160  			Repo:         t.Repo,
   161  			User:         t.Comment.User,
   162  			IssueAuthor:  t.Issue.User,
   163  			Assignees:    t.Issue.Assignees,
   164  			IssueState:   t.Issue.State,
   165  			IssueTitle:   t.Issue.Title,
   166  			IssueBody:    t.Issue.Body,
   167  			IssueHTMLURL: t.Issue.HTMLURL,
   168  		}, nil
   169  	case PullRequestEvent:
   170  		action := GeneralizeCommentAction(string(t.Action))
   171  		if action == "" {
   172  			return nil, fmt.Errorf("failed to determine events action [%v]", string(t.Action))
   173  		}
   174  
   175  		return &GenericCommentEvent{
   176  			ID:           t.PullRequest.ID,
   177  			NodeID:       t.PullRequest.NodeID,
   178  			GUID:         t.GUID,
   179  			IsPR:         true,
   180  			Action:       action,
   181  			Body:         t.PullRequest.Body,
   182  			HTMLURL:      t.PullRequest.HTMLURL,
   183  			Number:       t.PullRequest.Number,
   184  			Repo:         t.Repo,
   185  			User:         t.PullRequest.User,
   186  			IssueAuthor:  t.PullRequest.User,
   187  			Assignees:    t.PullRequest.Assignees,
   188  			IssueState:   t.PullRequest.State,
   189  			IssueTitle:   t.PullRequest.Title,
   190  			IssueBody:    t.PullRequest.Body,
   191  			IssueHTMLURL: t.PullRequest.HTMLURL,
   192  		}, nil
   193  	case ReviewEvent:
   194  		action := GeneralizeCommentAction(string(t.Action))
   195  		if action == "" {
   196  			return nil, fmt.Errorf("failed to determine events action [%v]", string(t.Action))
   197  		}
   198  
   199  		return &GenericCommentEvent{
   200  			GUID:         t.GUID,
   201  			NodeID:       t.Review.NodeID,
   202  			IsPR:         true,
   203  			Action:       action,
   204  			Body:         t.Review.Body,
   205  			HTMLURL:      t.Review.HTMLURL,
   206  			Number:       t.PullRequest.Number,
   207  			Repo:         t.Repo,
   208  			User:         t.Review.User,
   209  			IssueAuthor:  t.PullRequest.User,
   210  			Assignees:    t.PullRequest.Assignees,
   211  			IssueState:   t.PullRequest.State,
   212  			IssueTitle:   t.PullRequest.Title,
   213  			IssueBody:    t.PullRequest.Body,
   214  			IssueHTMLURL: t.PullRequest.HTMLURL,
   215  		}, nil
   216  	case ReviewCommentEvent:
   217  		action := GeneralizeCommentAction(string(t.Action))
   218  		if action == "" {
   219  			return nil, fmt.Errorf("failed to determine events action [%v]", string(t.Action))
   220  		}
   221  
   222  		return &GenericCommentEvent{
   223  			GUID:         t.GUID,
   224  			NodeID:       t.Comment.NodeID,
   225  			IsPR:         true,
   226  			CommentID:    &t.Comment.ID,
   227  			Action:       action,
   228  			Body:         t.Comment.Body,
   229  			HTMLURL:      t.Comment.HTMLURL,
   230  			Number:       t.PullRequest.Number,
   231  			Repo:         t.Repo,
   232  			User:         t.Comment.User,
   233  			IssueAuthor:  t.PullRequest.User,
   234  			Assignees:    t.PullRequest.Assignees,
   235  			IssueState:   t.PullRequest.State,
   236  			IssueTitle:   t.PullRequest.Title,
   237  			IssueBody:    t.PullRequest.Body,
   238  			IssueHTMLURL: t.PullRequest.HTMLURL,
   239  		}, nil
   240  	}
   241  
   242  	return nil, fmt.Errorf("we were unable to generalize comment, unknown type encountered")
   243  }