Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9968,6 +9968,28 @@ spec:
type: object
x-kubernetes-preserve-unknown-fields: true
type: object
trigger_comment:
description: |-
TriggerComment, when set, references the GitHub comment whose command
triggered this job (e.g. /test, /retest, /ok-to-test). It is nil for jobs
that were triggered automatically (e.g. when a PR is opened or updated, by
Tide, or for periodics).
properties:
html_url:
description: |-
HTMLURL is the HTML URL of the comment. Unlike the bare ID it is always
set for comment triggers and unambiguously identifies the comment
regardless of its kind (issue comment, review, or review comment).
type: string
id:
description: |-
ID is the numeric ID of the GitHub comment. It is set for issue/PR
comments and review comments, but may be 0 for triggers that have no
dedicated comment ID (e.g. a command in a review body or a PR
description). Together with Refs it can be used to construct the GitHub
API or HTML URL for the comment.
type: integer
type: object
type:
description: |-
Type is the type of job and informs how
Expand Down
23 changes: 22 additions & 1 deletion pkg/apis/prowjobs/v1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ func GetAllProwJobStates() []ProwJobState {
SuccessState,
FailureState,
AbortedState,
ErrorState}
ErrorState,
}
}

// ProwJobAgent specifies the controller (such as plank or jenkins-agent) that runs the job.
Expand Down Expand Up @@ -234,6 +235,26 @@ type ProwJobSpec struct {
// This behaviour may be superseded by MaxConcurrency field, if it
// is set to a constraining value.
JobQueueName string `json:"job_queue_name,omitempty"`

// TriggerComment, when set, references the GitHub comment whose command
// triggered this job (e.g. /test, /retest, /ok-to-test). It is nil for jobs
// that were triggered automatically (e.g. when a PR is opened or updated, by
// Tide, or for periodics).
TriggerComment *TriggerComment `json:"trigger_comment,omitempty"`
}

// TriggerComment references the GitHub comment whose command triggered a job.
type TriggerComment struct {
// ID is the numeric ID of the GitHub comment. It is set for issue/PR
// comments and review comments, but may be 0 for triggers that have no
// dedicated comment ID (e.g. a command in a review body or a PR
// description). Together with Refs it can be used to construct the GitHub
// API or HTML URL for the comment.
ID int `json:"id,omitempty"`
// HTMLURL is the HTML URL of the comment. Unlike the bare ID it is always
// set for comment triggers and unambiguously identifies the comment
// regardless of its kind (issue comment, review, or review comment).
HTMLURL string `json:"html_url,omitempty"`
}

func (pjs ProwJobSpec) HasPipelineRunSpec() bool {
Expand Down
21 changes: 21 additions & 0 deletions pkg/apis/prowjobs/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion pkg/plugins/trigger/generic-comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"sigs.k8s.io/prow/pkg/kube"

"k8s.io/apimachinery/pkg/util/sets"
prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
"sigs.k8s.io/prow/pkg/config"
"sigs.k8s.io/prow/pkg/github"
"sigs.k8s.io/prow/pkg/labels"
Expand Down Expand Up @@ -203,7 +204,11 @@ func handleGenericComment(c Client, cp commentPruner, trigger plugins.Trigger, g
})
}

return RunRequestedWithLabels(c, pr, baseSHA, toTest, gc.GUID, additionalLabels)
triggerComment := &prowapi.TriggerComment{HTMLURL: gc.HTMLURL}
if gc.CommentID != nil {
triggerComment.ID = *gc.CommentID
}
return RunRequestedWithLabels(c, pr, baseSHA, toTest, gc.GUID, additionalLabels, triggerComment)
}

func HonorOkToTest(trigger plugins.Trigger) bool {
Expand Down
13 changes: 8 additions & 5 deletions pkg/plugins/trigger/trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,17 @@ func validateContextOverlap(toRun, toSkip []config.Presubmit) error {

// RunRequested executes the config.Presubmits that are requested
func RunRequested(c Client, pr *github.PullRequest, baseSHA string, requestedJobs []config.Presubmit, eventGUID string) error {
return runRequested(c, pr, baseSHA, requestedJobs, eventGUID, nil)
return runRequested(c, pr, baseSHA, requestedJobs, eventGUID, nil, nil)
}

// RunRequestedWithLabels executes the config.Presubmits that are requested with the additional labels
func RunRequestedWithLabels(c Client, pr *github.PullRequest, baseSHA string, requestedJobs []config.Presubmit, eventGUID string, labels map[string]string) error {
return runRequested(c, pr, baseSHA, requestedJobs, eventGUID, labels)
// RunRequestedWithLabels executes the config.Presubmits that are requested with the additional labels.
// triggerComment, when non-nil, references the GitHub comment whose command triggered the jobs; pass
// nil when the jobs were not triggered by a comment.
func RunRequestedWithLabels(c Client, pr *github.PullRequest, baseSHA string, requestedJobs []config.Presubmit, eventGUID string, labels map[string]string, triggerComment *prowapi.TriggerComment) error {
return runRequested(c, pr, baseSHA, requestedJobs, eventGUID, labels, triggerComment)
}

func runRequested(c Client, pr *github.PullRequest, baseSHA string, requestedJobs []config.Presubmit, eventGUID string, labels map[string]string, millisecondOverride ...time.Duration) error {
func runRequested(c Client, pr *github.PullRequest, baseSHA string, requestedJobs []config.Presubmit, eventGUID string, labels map[string]string, triggerComment *prowapi.TriggerComment, millisecondOverride ...time.Duration) error {
var errors []error

// If the PR is not mergeable (e.g. due to merge conflicts),we will not trigger any jobs,
Expand All @@ -346,6 +348,7 @@ func runRequested(c Client, pr *github.PullRequest, baseSHA string, requestedJob
for _, job := range requestedJobs {
c.Logger.Infof("Starting %s build.", job.Name)
pj := pjutil.NewPresubmit(*pr, baseSHA, job, eventGUID, labels, pjutil.RequireScheduling(c.Config.Scheduler.Enabled))
pj.Spec.TriggerComment = triggerComment
c.Logger.WithFields(pjutil.ProwJobFields(&pj)).Info("Creating a new prowjob.")
if err := createWithRetry(context.TODO(), c.ProwJobClient, &pj, millisecondOverride...); err != nil {
c.Logger.WithError(err).Error("Failed to create prowjob.")
Expand Down
9 changes: 4 additions & 5 deletions pkg/plugins/trigger/trigger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ func TestHelpProvider(t *testing.T) {
}

func TestRunRequested(t *testing.T) {
var testCases = []struct {
testCases := []struct {
name string

pr *github.PullRequest
Expand Down Expand Up @@ -242,7 +242,7 @@ func TestRunRequested(t *testing.T) {
Logger: logrus.WithField("testcase", testCase.name),
}

err := runRequested(client, testCase.pr, fakegithub.TestRef, testCase.requestedJobs, "event-guid", nil, time.Nanosecond)
err := runRequested(client, testCase.pr, fakegithub.TestRef, testCase.requestedJobs, "event-guid", nil, nil, time.Nanosecond)
if err == nil && testCase.expectedErr {
t.Error("failed to receive an error")
}
Expand Down Expand Up @@ -271,7 +271,7 @@ func TestRunRequested(t *testing.T) {
}

func TestValidateContextOverlap(t *testing.T) {
var testCases = []struct {
testCases := []struct {
name string
toRun, toSkip []config.Presubmit
expectedErr bool
Expand Down Expand Up @@ -323,7 +323,7 @@ func TestValidateContextOverlap(t *testing.T) {
}

func TestTrustedUser(t *testing.T) {
var testcases = []struct {
testcases := []struct {
name string

onlyOrgMembers bool
Expand Down Expand Up @@ -633,7 +633,6 @@ func TestCreateWithRetry(t *testing.T) {

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {

fakeProwJobClient := fake.NewSimpleClientset()
fakeProwJobClient.PrependReactor("*", "*", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
if _, ok := action.(clienttesting.CreateActionImpl); ok && tc.numFailedCreate > 0 {
Expand Down
21 changes: 20 additions & 1 deletion pkg/pod-utils/downwardapi/jobspec.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ type JobSpec struct {

DecorationConfig *prowapi.DecorationConfig `json:"decoration_config,omitempty"`

// TriggerComment, when set, references the GitHub comment whose command
// triggered this job. It is only set for comment-triggered presubmits and
// is nil otherwise.
TriggerComment *prowapi.TriggerComment `json:"trigger_comment,omitempty"`

// we need to keep track of the agent until we
// migrate everyone away from using the $BUILD_NUMBER
// environment variable
Expand All @@ -64,6 +69,7 @@ func NewJobSpec(spec prowapi.ProwJobSpec, buildID, prowJobID string) JobSpec {
Refs: spec.Refs,
ExtraRefs: spec.ExtraRefs,
DecorationConfig: spec.DecorationConfig,
TriggerComment: spec.TriggerComment,
agent: spec.Agent,
}
}
Expand Down Expand Up @@ -109,6 +115,9 @@ const (
PullPullShaEnv = "PULL_PULL_SHA"
PullHeadRefEnv = "PULL_HEAD_REF"
PullTitleEnv = "PULL_TITLE"

TriggerCommentIDEnv = "TRIGGER_COMMENT_ID"
TriggerCommentURLEnv = "TRIGGER_COMMENT_URL"
)

// EnvForSpec returns a mapping of environment variables
Expand Down Expand Up @@ -180,14 +189,24 @@ func EnvForSpec(spec JobSpec) (map[string]string, error) {
env[PullHeadRefEnv] = spec.Refs.Pulls[0].HeadRef
env[PullTitleEnv] = spec.Refs.Pulls[0].Title

// Only set for jobs triggered by a comment command (e.g. /test, /retest).
if spec.TriggerComment != nil {
if spec.TriggerComment.ID != 0 {
env[TriggerCommentIDEnv] = strconv.Itoa(spec.TriggerComment.ID)
}
if spec.TriggerComment.HTMLURL != "" {
env[TriggerCommentURLEnv] = spec.TriggerComment.HTMLURL
}
}

return env, nil
}

// EnvForType returns the slice of environment variables to export for jobType
func EnvForType(jobType prowapi.ProwJobType) []string {
baseEnv := []string{CI, JobNameEnv, JobSpecEnv, JobTypeEnv, ProwJobIDEnv, BuildIDEnv, ProwBuildIDEnv}
refsEnv := []string{RepoOwnerEnv, RepoNameEnv, SrcBaseEnv, SrcHostEnv, PullBaseRefEnv, PullBaseShaEnv, PullRefsEnv}
pullEnv := []string{PullNumberEnv, PullPullShaEnv, PullHeadRefEnv, PullTitleEnv}
pullEnv := []string{PullNumberEnv, PullPullShaEnv, PullHeadRefEnv, PullTitleEnv, TriggerCommentIDEnv, TriggerCommentURLEnv}

switch jobType {
case prowapi.PeriodicJob:
Expand Down
100 changes: 98 additions & 2 deletions pkg/pod-utils/downwardapi/jobspec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
)

func TestEnvironmentForSpec(t *testing.T) {
var tests = []struct {
tests := []struct {
name string
spec JobSpec
expected map[string]string
Expand Down Expand Up @@ -159,6 +159,102 @@ func TestEnvironmentForSpec(t *testing.T) {
"PULL_TITLE": "pull-title",
},
},
{
name: "presubmit job triggered by a comment",
spec: JobSpec{
Type: prowapi.PresubmitJob,
Job: "job-name",
BuildID: "0",
ProwJobID: "prowjob",
Refs: &prowapi.Refs{
Org: "org-name",
Repo: "repo-name",
BaseRef: "base-ref",
BaseSHA: "base-sha",
Pulls: []prowapi.Pull{{
Number: 1,
Author: "author-name",
SHA: "pull-sha",
HeadRef: "branch-name",
Title: "pull-title",
}},
},
TriggerComment: &prowapi.TriggerComment{
ID: 987654321,
HTMLURL: "https://github.com/org-name/repo-name/pull/1#issuecomment-987654321",
},
},
expected: map[string]string{
"CI": "true",
"JOB_NAME": "job-name",
"BUILD_ID": "0",
"PROW_JOB_ID": "prowjob",
"JOB_TYPE": "presubmit",
"JOB_SPEC": `{"type":"presubmit","job":"job-name","buildid":"0","prowjobid":"prowjob","refs":{"org":"org-name","repo":"repo-name","base_ref":"base-ref","base_sha":"base-sha","pulls":[{"number":1,"author":"author-name","sha":"pull-sha","title":"pull-title","head_ref":"branch-name"}]},"trigger_comment":{"id":987654321,"html_url":"https://github.com/org-name/repo-name/pull/1#issuecomment-987654321"}}`,
"REPO_OWNER": "org-name",
"REPO_NAME": "repo-name",
"SRC_BASE": "org-name/repo-name",
"SRC_HOST": "github.com",
"PULL_BASE_REF": "base-ref",
"PULL_BASE_SHA": "base-sha",
"PULL_REFS": "base-ref:base-sha,1:pull-sha",
"PULL_HEAD_REF": "branch-name",
"PULL_NUMBER": "1",
"PULL_PULL_SHA": "pull-sha",
"PULL_TITLE": "pull-title",
"TRIGGER_COMMENT_ID": "987654321",
"TRIGGER_COMMENT_URL": "https://github.com/org-name/repo-name/pull/1#issuecomment-987654321",
},
},
{
// Review-body and PR-body events have a URL but no comment ID (ID==0).
// Only TRIGGER_COMMENT_URL should be emitted; TRIGGER_COMMENT_ID must be absent.
name: "presubmit job triggered by a review (url-only, no comment id)",
spec: JobSpec{
Type: prowapi.PresubmitJob,
Job: "job-name",
BuildID: "0",
ProwJobID: "prowjob",
Refs: &prowapi.Refs{
Org: "org-name",
Repo: "repo-name",
BaseRef: "base-ref",
BaseSHA: "base-sha",
Pulls: []prowapi.Pull{{
Number: 1,
Author: "author-name",
SHA: "pull-sha",
HeadRef: "branch-name",
Title: "pull-title",
}},
},
TriggerComment: &prowapi.TriggerComment{
// ID intentionally zero — mirrors a PR review event
HTMLURL: "https://github.com/org-name/repo-name/pull/1#pullrequestreview-111",
},
},
expected: map[string]string{
"CI": "true",
"JOB_NAME": "job-name",
"BUILD_ID": "0",
"PROW_JOB_ID": "prowjob",
"JOB_TYPE": "presubmit",
"JOB_SPEC": `{"type":"presubmit","job":"job-name","buildid":"0","prowjobid":"prowjob","refs":{"org":"org-name","repo":"repo-name","base_ref":"base-ref","base_sha":"base-sha","pulls":[{"number":1,"author":"author-name","sha":"pull-sha","title":"pull-title","head_ref":"branch-name"}]},"trigger_comment":{"html_url":"https://github.com/org-name/repo-name/pull/1#pullrequestreview-111"}}`,
"REPO_OWNER": "org-name",
"REPO_NAME": "repo-name",
"SRC_BASE": "org-name/repo-name",
"SRC_HOST": "github.com",
"PULL_BASE_REF": "base-ref",
"PULL_BASE_SHA": "base-sha",
"PULL_REFS": "base-ref:base-sha,1:pull-sha",
"PULL_HEAD_REF": "branch-name",
"PULL_NUMBER": "1",
"PULL_PULL_SHA": "pull-sha",
"PULL_TITLE": "pull-title",
"TRIGGER_COMMENT_URL": "https://github.com/org-name/repo-name/pull/1#pullrequestreview-111",
// TRIGGER_COMMENT_ID must NOT be present when ID is zero
},
},
{
name: "postsubmit job with path alias",
spec: JobSpec{
Expand Down Expand Up @@ -272,7 +368,7 @@ func TestEnvironmentForSpec(t *testing.T) {
}

func TestGetRevisionFromSpec(t *testing.T) {
var tests = []struct {
tests := []struct {
name string
spec JobSpec
expected string
Expand Down
10 changes: 10 additions & 0 deletions site/content/en/docs/jobs.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,16 @@ the build.
| `PULL_PULL_SHA` | | | | ✓ | Pull request head SHA. | `qwe456` |
| `PULL_HEAD_REF` | | | | ✓ | Pull request branch name. | `fixup-some-stuff` |
| `PULL_TITLE` | | | | ✓ | Pull request title. | `Add something` |
| `TRIGGER_COMMENT_ID` | | | | ✓ | ID of the GitHub comment whose command triggered the job. Only set when the job was triggered by a comment (e.g. `/test`, `/retest`). | `987654321` |
| `TRIGGER_COMMENT_URL` | | | | ✓ | HTML URL of the GitHub comment whose command triggered the job. Only set for comment-triggered jobs. | `https://github.com/org/repo/pull/5#issuecomment-987654321` |

Note: `TRIGGER_COMMENT_ID` and `TRIGGER_COMMENT_URL` (and the corresponding
`trigger_comment` object with `id` / `html_url` fields in `JOB_SPEC`) are only
present for presubmits that were triggered by a comment command. They are absent
for jobs triggered automatically (on PR creation/update, by Tide, or for
periodics). The ID alone is ambiguous about the comment kind, so consumers that
need to call the GitHub API should prefer the URL or combine the ID with the
refs in `JOB_SPEC`.

Examples of the JSON-encoded job specification follow for the different
job types:
Expand Down
Loading