Merge remote-tracking branch 'remotes/from/ce/main'
Some checks failed
build / setup (push) Has been cancelled
build / hcp-setup (push) Has been cancelled
CI / setup (push) Has been cancelled
Run linters / Setup (push) Has been cancelled
Run linters / Semgrep (push) Has been cancelled
Check Copywrite Headers / copywrite (push) Has been cancelled
Security Scan / scan (push) Has been cancelled
build / Check ce/* Pull Requests (push) Has been cancelled
build / ui (push) Has been cancelled
build / artifacts-ce (push) Has been cancelled
build / artifacts-ent (push) Has been cancelled
build / hcp-image (push) Has been cancelled
build / test (push) Has been cancelled
build / test-hcp-image (push) Has been cancelled
build / completed-successfully (push) Has been cancelled
CI / Run Autopilot upgrade tool (push) Has been cancelled
CI / Run Go tests (push) Has been cancelled
CI / Run Go tests tagged with testonly (push) Has been cancelled
CI / Run Go tests with data race detection (push) Has been cancelled
CI / Run Go tests with FIPS configuration (push) Has been cancelled
CI / Test UI (push) Has been cancelled
CI / tests-completed (push) Has been cancelled
Run linters / Deprecated functions (push) Has been cancelled
Run linters / Code checks (push) Has been cancelled
Run linters / Protobuf generate delta (push) Has been cancelled
Run linters / Format (push) Has been cancelled

This commit is contained in:
hc-github-team-secure-vault-core 2026-05-22 17:32:28 +00:00
commit 8e20a13896
4 changed files with 249 additions and 8 deletions

View file

@ -0,0 +1,188 @@
// Copyright IBM Corp. 2016, 2026
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"net/http"
"net/http/httptest"
"net/url"
"testing"
libgithub "github.com/google/go-github/v83/github"
"github.com/stretchr/testify/require"
)
// Test_addAssignees tests the addAssignees helper function with various input scenarios
// including single/multiple assignees, empty lists, filtering of empty strings, and
// deduplication of logins.
func Test_addAssignees(t *testing.T) {
t.Parallel()
for name, test := range map[string]struct {
logins []string
shouldCall bool
expectedError bool
}{
"single assignee": {
logins: []string{"user1"},
shouldCall: true,
},
"multiple assignees": {
logins: []string{"user1", "user2", "user3"},
shouldCall: true,
},
"empty login list": {
logins: []string{},
shouldCall: false,
},
"nil login list": {
logins: nil,
shouldCall: false,
},
"logins with empty strings": {
logins: []string{"user1", "", "user2", ""},
shouldCall: true, // Empty strings should be filtered out
},
"only empty strings": {
logins: []string{"", "", ""},
shouldCall: false, // All empty, should skip
},
"duplicate logins": {
logins: []string{"user1", "user1", "user2"},
shouldCall: true, // Duplicates should be compacted
},
} {
t.Run(name, func(t *testing.T) {
t.Parallel()
called := false
client, mux, teardown := setupTestClient(t)
defer teardown()
if test.shouldCall {
mux.HandleFunc("/repos/test-owner/test-repo/issues/123/assignees", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
called = true
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"assignees": []}`))
})
}
err := addAssignees(
context.Background(),
client,
"test-owner",
"test-repo",
123,
test.logins,
)
if test.expectedError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
require.Equal(t, test.shouldCall, called, "API call expectation mismatch")
})
}
}
// Test_addReviewers tests the addReviewers helper function with various input scenarios
// including single/multiple reviewers, empty lists, filtering of empty strings, and
// deduplication of logins.
func Test_addReviewers(t *testing.T) {
t.Parallel()
for name, test := range map[string]struct {
logins []string
shouldCall bool
expectedError bool
}{
"single reviewer": {
logins: []string{"user1"},
shouldCall: true,
},
"multiple reviewers": {
logins: []string{"user1", "user2", "user3"},
shouldCall: true,
},
"empty login list": {
logins: []string{},
shouldCall: false,
},
"nil login list": {
logins: nil,
shouldCall: false,
},
"logins with empty strings": {
logins: []string{"user1", "", "user2", ""},
shouldCall: true, // Empty strings should be filtered out
},
"only empty strings": {
logins: []string{"", "", ""},
shouldCall: false, // All empty, should skip
},
"duplicate logins": {
logins: []string{"user1", "user1", "user2"},
shouldCall: true, // Duplicates should be compacted
},
} {
t.Run(name, func(t *testing.T) {
t.Parallel()
called := false
client, mux, teardown := setupTestClient(t)
defer teardown()
if test.shouldCall {
mux.HandleFunc("/repos/test-owner/test-repo/pulls/123/requested_reviewers", func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodPost, r.Method)
called = true
w.WriteHeader(http.StatusCreated)
w.Write([]byte(`{"users": [], "teams": []}`))
})
}
err := addReviewers(
context.Background(),
client,
"test-owner",
"test-repo",
123,
test.logins,
)
if test.expectedError {
require.Error(t, err)
} else {
require.NoError(t, err)
}
require.Equal(t, test.shouldCall, called, "API call expectation mismatch")
})
}
}
// setupTestClient creates a test GitHub client with a mock HTTP server for testing.
// It returns the client, the HTTP mux for registering handlers, and a teardown function
// that should be called to clean up the server when the test completes.
func setupTestClient(t *testing.T) (*libgithub.Client, *http.ServeMux, func()) {
t.Helper()
mux := http.NewServeMux()
server := httptest.NewServer(mux)
client := libgithub.NewClient(nil)
serverURL, err := url.Parse(server.URL + "/")
require.NoError(t, err)
client.BaseURL = serverURL
teardown := func() {
server.Close()
}
return client, mux, teardown
}

View file

@ -0,0 +1,39 @@
// Copyright IBM Corp. 2016, 2026
// SPDX-License-Identifier: BUSL-1.1
package github
import (
"context"
"log/slog"
"slices"
libgithub "github.com/google/go-github/v83/github"
slogctx "github.com/veqryn/slog-context"
)
// addReviewers requests reviews from the given logins on the pull request
func addReviewers(
ctx context.Context,
github *libgithub.Client,
owner string,
repo string,
number int,
logins []string,
) error {
logins = slices.Compact(slices.DeleteFunc(logins, func(a string) bool {
return a == ""
}))
ctx = slogctx.Append(ctx, slog.Any("reviewer-logins", logins))
if len(logins) < 1 {
slog.Default().InfoContext(ctx, "skipping pull request review requests because no logins were provided")
return nil
}
slog.Default().DebugContext(ctx, "requesting reviews on pull request")
_, _, err := github.PullRequests.RequestReviewers(ctx, owner, repo, number, libgithub.ReviewersRequest{
Reviewers: logins,
})
return err
}

View file

@ -759,6 +759,20 @@ func (r *CreateBackportReq) backportRef(
return res
}
// Request review from the PR author
err = addReviewers(
ctx,
github,
r.Owner,
r.Repo,
int(res.PullRequest.GetNumber()),
[]string{pr.GetUser().GetLogin()},
)
if err != nil {
res.Error = fmt.Errorf("requesting review from PR author on backport pull request %w", err)
return res
}
// Copy non-backport labels from the original PR to the backport PR
labelsToAdd := filterNonBackportLabels(pr.Labels, r.BackportLabelPrefix)
err = addLabelsToIssue(

View file

@ -44,7 +44,7 @@ importers:
version: 3.0.0
'@hashicorp/vault-client-typescript':
specifier: github:hashicorp/vault-client-typescript
version: https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/0ba8e35e7fcf5b93110bb7fbcc2608184fd5ae07
version: https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/d0d038ea91aa92ecd0fd5d9e8f35b75a8c7e559c
ember-auto-import:
specifier: 2.10.0
version: 2.10.0(@glint/template@1.7.3)(webpack@5.105.4)
@ -1601,8 +1601,8 @@ packages:
'@hashicorp/flight-icons@3.14.0':
resolution: {integrity: sha512-nyLDApaZsAHpAf2sRNwYX1MnJQU9UI3euiwE6wHPl2l/+Yt8wba1oXkmWL/Ptc4QgJxxnRUUhf66jGcB/AIOyQ==}
'@hashicorp/vault-client-typescript@https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/0ba8e35e7fcf5b93110bb7fbcc2608184fd5ae07':
resolution: {tarball: https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/0ba8e35e7fcf5b93110bb7fbcc2608184fd5ae07}
'@hashicorp/vault-client-typescript@https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/d0d038ea91aa92ecd0fd5d9e8f35b75a8c7e559c':
resolution: {tarball: https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/d0d038ea91aa92ecd0fd5d9e8f35b75a8c7e559c}
version: 0.0.0
'@humanwhocodes/config-array@0.13.0':
@ -7201,8 +7201,8 @@ packages:
engines: {node: '>=10'}
hasBin: true
semver@7.8.0:
resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==}
semver@7.8.1:
resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==}
engines: {node: '>=10'}
hasBin: true
@ -11269,7 +11269,7 @@ snapshots:
'@hashicorp/flight-icons@3.14.0': {}
'@hashicorp/vault-client-typescript@https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/0ba8e35e7fcf5b93110bb7fbcc2608184fd5ae07': {}
'@hashicorp/vault-client-typescript@https://codeload.github.com/hashicorp/vault-client-typescript/tar.gz/d0d038ea91aa92ecd0fd5d9e8f35b75a8c7e559c': {}
'@humanwhocodes/config-array@0.13.0':
dependencies:
@ -18467,7 +18467,7 @@ snapshots:
semver@7.7.4: {}
semver@7.8.0: {}
semver@7.8.1: {}
send@0.19.0:
dependencies:
@ -19420,7 +19420,7 @@ snapshots:
espree: 9.6.1
esquery: 1.7.0
lodash: 4.18.0
semver: 7.8.0
semver: 7.8.1
transitivePeerDependencies:
- supports-color