Merge pull request #198 from camandel/cmd_disable_vars_pr

cmd: project option to disable passing variables to PR from forked repo
This commit is contained in:
Simone Gotti 2020-01-28 15:08:58 +01:00 committed by GitHub
commit ec53a63053
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 473 additions and 31 deletions

View File

@ -41,6 +41,7 @@ type projectCreateOptions struct {
remoteSourceName string remoteSourceName string
skipSSHHostKeyCheck bool skipSSHHostKeyCheck bool
visibility string visibility string
passVarsToForkedPR bool
} }
var projectCreateOpts projectCreateOptions var projectCreateOpts projectCreateOptions
@ -54,6 +55,7 @@ func init() {
flags.BoolVarP(&projectCreateOpts.skipSSHHostKeyCheck, "skip-ssh-host-key-check", "s", false, "skip ssh host key check") flags.BoolVarP(&projectCreateOpts.skipSSHHostKeyCheck, "skip-ssh-host-key-check", "s", false, "skip ssh host key check")
flags.StringVar(&projectCreateOpts.parentPath, "parent", "", `parent project group path (i.e "org/org01" for root project group in org01, "user/user01/group01/subgroub01") or project group id where the project should be created`) flags.StringVar(&projectCreateOpts.parentPath, "parent", "", `parent project group path (i.e "org/org01" for root project group in org01, "user/user01/group01/subgroub01") or project group id where the project should be created`)
flags.StringVar(&projectCreateOpts.visibility, "visibility", "public", `project visibility (public or private)`) flags.StringVar(&projectCreateOpts.visibility, "visibility", "public", `project visibility (public or private)`)
flags.BoolVar(&projectCreateOpts.passVarsToForkedPR, "pass-vars-to-forked-pr", false, `pass variables to run even if triggered by PR from forked repo`)
if err := cmdProjectCreate.MarkFlagRequired("name"); err != nil { if err := cmdProjectCreate.MarkFlagRequired("name"); err != nil {
log.Fatal(err) log.Fatal(err)
@ -96,6 +98,7 @@ func projectCreate(cmd *cobra.Command, args []string) error {
RepoPath: projectCreateOpts.repoPath, RepoPath: projectCreateOpts.repoPath,
RemoteSourceName: projectCreateOpts.remoteSourceName, RemoteSourceName: projectCreateOpts.remoteSourceName,
SkipSSHHostKeyCheck: projectCreateOpts.skipSSHHostKeyCheck, SkipSSHHostKeyCheck: projectCreateOpts.skipSSHHostKeyCheck,
PassVarsToForkedPR: projectCreateOpts.passVarsToForkedPR,
} }
log.Infof("creating project") log.Infof("creating project")

View File

@ -37,9 +37,10 @@ var cmdProjectUpdate = &cobra.Command{
type projectUpdateOptions struct { type projectUpdateOptions struct {
ref string ref string
name string name string
parentPath string parentPath string
visibility string visibility string
passVarsToForkedPR bool
} }
var projectUpdateOpts projectUpdateOptions var projectUpdateOpts projectUpdateOptions
@ -51,6 +52,7 @@ func init() {
flags.StringVarP(&projectUpdateOpts.name, "name", "n", "", "project name") flags.StringVarP(&projectUpdateOpts.name, "name", "n", "", "project name")
flags.StringVar(&projectUpdateOpts.parentPath, "parent", "", `parent project group path (i.e "org/org01" for root project group in org01, "user/user01/group01/subgroub01") or project group id where the project should be moved`) flags.StringVar(&projectUpdateOpts.parentPath, "parent", "", `parent project group path (i.e "org/org01" for root project group in org01, "user/user01/group01/subgroub01") or project group id where the project should be moved`)
flags.StringVar(&projectUpdateOpts.visibility, "visibility", "public", `project visibility (public or private)`) flags.StringVar(&projectUpdateOpts.visibility, "visibility", "public", `project visibility (public or private)`)
flags.BoolVar(&projectUpdateOpts.passVarsToForkedPR, "pass-vars-to-forked-pr", false, `pass variables to run even if triggered by PR from forked repo`)
if err := cmdProjectUpdate.MarkFlagRequired("ref"); err != nil { if err := cmdProjectUpdate.MarkFlagRequired("ref"); err != nil {
log.Fatal(err) log.Fatal(err)
@ -78,6 +80,9 @@ func projectUpdate(cmd *cobra.Command, args []string) error {
visibility := gwapitypes.Visibility(projectUpdateOpts.visibility) visibility := gwapitypes.Visibility(projectUpdateOpts.visibility)
req.Visibility = &visibility req.Visibility = &visibility
} }
if flags.Changed("pass-vars-to-forked-pr") {
req.PassVarsToForkedPR = &projectUpdateOpts.passVarsToForkedPR
}
log.Infof("updating project") log.Infof("updating project")
project, _, err := gwclient.UpdateProject(context.TODO(), projectUpdateOpts.ref, req) project, _, err := gwclient.UpdateProject(context.TODO(), projectUpdateOpts.ref, req)

View File

@ -156,6 +156,10 @@ func webhookDataFromPullRequest(hook *pullRequestHook) *types.WebhookData {
if sender == "" { if sender == "" {
sender = hook.Sender.Login sender = hook.Sender.Login
} }
prFromSameRepo := false
if hook.PullRequest.Base.Repo.URL == hook.PullRequest.Head.Repo.URL {
prFromSameRepo = true
}
whd := &types.WebhookData{ whd := &types.WebhookData{
Event: types.WebhookEventPullRequest, Event: types.WebhookEventPullRequest,
CommitSHA: hook.PullRequest.Head.Sha, CommitSHA: hook.PullRequest.Head.Sha,
@ -166,11 +170,13 @@ func webhookDataFromPullRequest(hook *pullRequestHook) *types.WebhookData {
Sender: sender, Sender: sender,
PullRequestID: strconv.FormatInt(hook.PullRequest.ID, 10), PullRequestID: strconv.FormatInt(hook.PullRequest.ID, 10),
PullRequestLink: hook.PullRequest.URL, PullRequestLink: hook.PullRequest.URL,
PRFromSameRepo: prFromSameRepo,
Repo: types.WebhookDataRepo{ Repo: types.WebhookDataRepo{
Path: path.Join(hook.Repo.Owner.Username, hook.Repo.Name), Path: path.Join(hook.Repo.Owner.Username, hook.Repo.Name),
WebURL: hook.Repo.URL, WebURL: hook.Repo.URL,
}, },
} }
return whd return whd
} }

View File

@ -116,6 +116,10 @@ func webhookDataFromPullRequest(hook *github.PullRequestEvent) (*types.WebhookDa
if sender == nil { if sender == nil {
sender = hook.Sender.Login sender = hook.Sender.Login
} }
prFromSameRepo := false
if hook.PullRequest.Base.Repo.URL == hook.PullRequest.Head.Repo.URL {
prFromSameRepo = true
}
whd := &types.WebhookData{ whd := &types.WebhookData{
Event: types.WebhookEventPullRequest, Event: types.WebhookEventPullRequest,
@ -127,6 +131,7 @@ func webhookDataFromPullRequest(hook *github.PullRequestEvent) (*types.WebhookDa
Sender: *sender, Sender: *sender,
PullRequestID: strconv.Itoa(*hook.PullRequest.Number), PullRequestID: strconv.Itoa(*hook.PullRequest.Number),
PullRequestLink: *hook.PullRequest.HTMLURL, PullRequestLink: *hook.PullRequest.HTMLURL,
PRFromSameRepo: prFromSameRepo,
Repo: types.WebhookDataRepo{ Repo: types.WebhookDataRepo{
Path: path.Join(*hook.Repo.Owner.Login, *hook.Repo.Name), Path: path.Join(*hook.Repo.Owner.Login, *hook.Repo.Name),

View File

@ -140,7 +140,12 @@ func webhookDataFromPullRequest(hook *pullRequestHook) *types.WebhookData {
if sender == "" { if sender == "" {
sender = hook.User.Username sender = hook.User.Username
} }
build := &types.WebhookData{ prFromSameRepo := false
if hook.ObjectAttributes.Source.URL == hook.ObjectAttributes.Target.URL {
prFromSameRepo = true
}
whd := &types.WebhookData{
Event: types.WebhookEventPullRequest, Event: types.WebhookEventPullRequest,
CommitSHA: hook.ObjectAttributes.LastCommit.ID, CommitSHA: hook.ObjectAttributes.LastCommit.ID,
SSHURL: hook.Project.SSHURL, SSHURL: hook.Project.SSHURL,
@ -150,11 +155,12 @@ func webhookDataFromPullRequest(hook *pullRequestHook) *types.WebhookData {
Sender: sender, Sender: sender,
PullRequestID: strconv.Itoa(hook.ObjectAttributes.Iid), PullRequestID: strconv.Itoa(hook.ObjectAttributes.Iid),
PullRequestLink: hook.ObjectAttributes.URL, PullRequestLink: hook.ObjectAttributes.URL,
PRFromSameRepo: prFromSameRepo,
Repo: types.WebhookDataRepo{ Repo: types.WebhookDataRepo{
Path: hook.Project.PathWithNamespace, Path: hook.Project.PathWithNamespace,
WebURL: hook.Project.WebURL, WebURL: hook.Project.WebURL,
}, },
} }
return build return whd
} }

View File

@ -57,6 +57,7 @@ type CreateProjectRequest struct {
RemoteSourceName string RemoteSourceName string
RepoPath string RepoPath string
SkipSSHHostKeyCheck bool SkipSSHHostKeyCheck bool
PassVarsToForkedPR bool
} }
func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectRequest) (*csapitypes.Project, error) { func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectRequest) (*csapitypes.Project, error) {
@ -150,6 +151,7 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq
RepositoryPath: req.RepoPath, RepositoryPath: req.RepoPath,
SkipSSHHostKeyCheck: req.SkipSSHHostKeyCheck, SkipSSHHostKeyCheck: req.SkipSSHHostKeyCheck,
SSHPrivateKey: string(privateKey), SSHPrivateKey: string(privateKey),
PassVarsToForkedPR: req.PassVarsToForkedPR,
} }
h.log.Infof("creating project") h.log.Infof("creating project")
@ -183,7 +185,8 @@ type UpdateProjectRequest struct {
Name *string Name *string
ParentRef *string ParentRef *string
Visibility *cstypes.Visibility Visibility *cstypes.Visibility
PassVarsToForkedPR *bool
} }
func (h *ActionHandler) UpdateProject(ctx context.Context, projectRef string, req *UpdateProjectRequest) (*csapitypes.Project, error) { func (h *ActionHandler) UpdateProject(ctx context.Context, projectRef string, req *UpdateProjectRequest) (*csapitypes.Project, error) {
@ -209,6 +212,9 @@ func (h *ActionHandler) UpdateProject(ctx context.Context, projectRef string, re
if req.Visibility != nil { if req.Visibility != nil {
p.Visibility = *req.Visibility p.Visibility = *req.Visibility
} }
if req.PassVarsToForkedPR != nil {
p.PassVarsToForkedPR = *req.PassVarsToForkedPR
}
h.log.Infof("updating project") h.log.Infof("updating project")
rp, resp, err := h.configstoreClient.UpdateProject(ctx, p.ID, p.Project) rp, resp, err := h.configstoreClient.UpdateProject(ctx, p.ID, p.Project)

View File

@ -338,6 +338,7 @@ type CreateRunRequest struct {
Tag string Tag string
Ref string Ref string
PullRequestID string PullRequestID string
PRFromSameRepo bool
SSHPrivKey string SSHPrivKey string
SSHHostKey string SSHHostKey string
SkipSSHHostKeyCheck bool SkipSSHHostKeyCheck bool
@ -429,10 +430,12 @@ func (h *ActionHandler) CreateRuns(ctx context.Context, req *CreateRunRequest) e
var variables map[string]string var variables map[string]string
if req.RunType == itypes.RunTypeProject { if req.RunType == itypes.RunTypeProject {
var err error if req.RefType != itypes.RunRefTypePullRequest || req.PRFromSameRepo || req.Project.PassVarsToForkedPR {
variables, err = h.genRunVariables(ctx, req) var err error
if err != nil { variables, err = h.genRunVariables(ctx, req)
return err if err != nil {
return err
}
} }
} else { } else {
variables = req.Variables variables = req.Variables

View File

@ -55,6 +55,7 @@ func (h *CreateProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
RepoPath: req.RepoPath, RepoPath: req.RepoPath,
RemoteSourceName: req.RemoteSourceName, RemoteSourceName: req.RemoteSourceName,
SkipSSHHostKeyCheck: req.SkipSSHHostKeyCheck, SkipSSHHostKeyCheck: req.SkipSSHHostKeyCheck,
PassVarsToForkedPR: req.PassVarsToForkedPR,
} }
project, err := h.ah.CreateProject(ctx, areq) project, err := h.ah.CreateProject(ctx, areq)
@ -101,9 +102,10 @@ func (h *UpdateProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
} }
areq := &action.UpdateProjectRequest{ areq := &action.UpdateProjectRequest{
Name: req.Name, Name: req.Name,
ParentRef: req.ParentRef, ParentRef: req.ParentRef,
Visibility: visibility, Visibility: visibility,
PassVarsToForkedPR: req.PassVarsToForkedPR,
} }
project, err := h.ah.UpdateProject(ctx, projectRef, areq) project, err := h.ah.UpdateProject(ctx, projectRef, areq)
if httpError(w, err) { if httpError(w, err) {
@ -235,12 +237,13 @@ func (h *ProjectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
func createProjectResponse(r *csapitypes.Project) *gwapitypes.ProjectResponse { func createProjectResponse(r *csapitypes.Project) *gwapitypes.ProjectResponse {
res := &gwapitypes.ProjectResponse{ res := &gwapitypes.ProjectResponse{
ID: r.ID, ID: r.ID,
Name: r.Name, Name: r.Name,
Path: r.Path, Path: r.Path,
ParentPath: r.ParentPath, ParentPath: r.ParentPath,
Visibility: gwapitypes.Visibility(r.Visibility), Visibility: gwapitypes.Visibility(r.Visibility),
GlobalVisibility: string(r.GlobalVisibility), GlobalVisibility: string(r.GlobalVisibility),
PassVarsToForkedPR: r.PassVarsToForkedPR,
} }
return res return res

View File

@ -127,6 +127,7 @@ func (h *webhooksHandler) handleWebhook(r *http.Request) error {
Branch: webhookData.Branch, Branch: webhookData.Branch,
Tag: webhookData.Tag, Tag: webhookData.Tag,
PullRequestID: webhookData.PullRequestID, PullRequestID: webhookData.PullRequestID,
PRFromSameRepo: webhookData.PRFromSameRepo,
Ref: webhookData.Ref, Ref: webhookData.Ref,
SSHPrivKey: sshPrivKey, SSHPrivKey: sshPrivKey,
SSHHostKey: sshHostKey, SSHHostKey: sshHostKey,

View File

@ -43,6 +43,7 @@ type WebhookData struct {
// use a string if on some platform (current or future) some PRs id will not be numbers // use a string if on some platform (current or future) some PRs id will not be numbers
PullRequestID string `json:"pull_request_id,omitempty"` PullRequestID string `json:"pull_request_id,omitempty"`
PullRequestLink string `json:"link,omitempty"` // Link to pull request PullRequestLink string `json:"link,omitempty"` // Link to pull request
PRFromSameRepo bool `json:"pr_from_same_repo,omitempty"`
Repo WebhookDataRepo `json:"repo,omitempty"` Repo WebhookDataRepo `json:"repo,omitempty"`
} }

View File

@ -307,6 +307,8 @@ type Project struct {
// Webhooksecret is the secret passed to git sources that support a // Webhooksecret is the secret passed to git sources that support a
// secret/token for signing or verifying the webhook payload // secret/token for signing or verifying the webhook payload
WebhookSecret string `json:"webhook_secret,omitempty"` WebhookSecret string `json:"webhook_secret,omitempty"`
PassVarsToForkedPR bool `json:"pass_vars_to_forked_pr,omitempty"`
} }
type SecretType string type SecretType string

View File

@ -21,21 +21,24 @@ type CreateProjectRequest struct {
RepoPath string `json:"repo_path,omitempty"` RepoPath string `json:"repo_path,omitempty"`
RemoteSourceName string `json:"remote_source_name,omitempty"` RemoteSourceName string `json:"remote_source_name,omitempty"`
SkipSSHHostKeyCheck bool `json:"skip_ssh_host_key_check,omitempty"` SkipSSHHostKeyCheck bool `json:"skip_ssh_host_key_check,omitempty"`
PassVarsToForkedPR bool `json:"pass_vars_to_forked_pr,omitempty"`
} }
type UpdateProjectRequest struct { type UpdateProjectRequest struct {
Name *string `json:"name,omitempty"` Name *string `json:"name,omitempty"`
ParentRef *string `json:"parent_ref,omitempty"` ParentRef *string `json:"parent_ref,omitempty"`
Visibility *Visibility `json:"visibility,omitempty"` Visibility *Visibility `json:"visibility,omitempty"`
PassVarsToForkedPR *bool `json:"pass_vars_to_forked_pr,omitempty"`
} }
type ProjectResponse struct { type ProjectResponse struct {
ID string `json:"id,omitempty"` ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Path string `json:"path,omitempty"` Path string `json:"path,omitempty"`
ParentPath string `json:"parent_path,omitempty"` ParentPath string `json:"parent_path,omitempty"`
Visibility Visibility `json:"visibility,omitempty"` Visibility Visibility `json:"visibility,omitempty"`
GlobalVisibility string `json:"global_visibility,omitempty"` GlobalVisibility string `json:"global_visibility,omitempty"`
PassVarsToForkedPR bool `json:"pass_vars_to_forked_pr,omitempty"`
} }
type ProjectCreateRunRequest struct { type ProjectCreateRunRequest struct {

View File

@ -49,6 +49,7 @@ import (
"gopkg.in/src-d/go-billy.v4/osfs" "gopkg.in/src-d/go-billy.v4/osfs"
"gopkg.in/src-d/go-git.v4" "gopkg.in/src-d/go-git.v4"
gitconfig "gopkg.in/src-d/go-git.v4/config" gitconfig "gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/cache" "gopkg.in/src-d/go-git.v4/plumbing/cache"
"gopkg.in/src-d/go-git.v4/plumbing/object" "gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/transport/http" "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
@ -58,6 +59,8 @@ import (
const ( const (
giteaUser01 = "user01" giteaUser01 = "user01"
giteaUser02 = "user02"
agolaUser01 = "user01" agolaUser01 = "user01"
) )
@ -447,9 +450,64 @@ func TestCreateProject(t *testing.T) {
createProject(ctx, t, giteaClient, gwClient) createProject(ctx, t, giteaClient, gwClient)
} }
func TestUpdateProject(t *testing.T) {
tests := []struct {
name string
passVarsToForkedPR bool
expected_pre bool
expected_post bool
}{
{
name: "test project update with pass-vars-to-forked-pr true",
passVarsToForkedPR: true,
expected_pre: false,
expected_post: true,
},
{
name: "test project update with pass-vars-to-forked-pr false",
passVarsToForkedPR: false,
expected_pre: false,
expected_post: false,
},
}
for _, tt := range tests {
dir, err := ioutil.TempDir("", "agola")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
defer os.RemoveAll(dir)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tetcd, tgitea, c := setup(ctx, t, dir)
defer shutdownGitea(tgitea)
defer shutdownEtcd(tetcd)
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
_, project := createProject(ctx, t, giteaClient, gwClient)
if project.PassVarsToForkedPR != tt.expected_pre {
t.Fatalf("expected PassVarsToForkedPR %v, got %v (pre-update)", tt.expected_pre, project.PassVarsToForkedPR)
}
project = updateProject(ctx, t, giteaClient, gwClient, project.ID, tt.passVarsToForkedPR)
if project.PassVarsToForkedPR != tt.expected_post {
t.Fatalf("expected PassVarsToForkedPR %v, got %v (port-update)", tt.expected_post, project.PassVarsToForkedPR)
}
}
}
func createProject(ctx context.Context, t *testing.T, giteaClient *gitea.Client, gwClient *gwclient.Client) (*gitea.Repository, *gwapitypes.ProjectResponse) { func createProject(ctx context.Context, t *testing.T, giteaClient *gitea.Client, gwClient *gwclient.Client) (*gitea.Repository, *gwapitypes.ProjectResponse) {
giteaRepo, err := giteaClient.CreateRepo(gitea.CreateRepoOption{ giteaRepo, err := giteaClient.CreateRepo(gitea.CreateRepoOption{
Name: "repo01", Name: "repo01",
Private: false,
}) })
if err != nil { if err != nil {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)
@ -470,7 +528,18 @@ func createProject(ctx context.Context, t *testing.T, giteaClient *gitea.Client,
return giteaRepo, project return giteaRepo, project
} }
func push(t *testing.T, config, cloneURL, remoteToken, message string) { func updateProject(ctx context.Context, t *testing.T, giteaClient *gitea.Client, gwClient *gwclient.Client, projectRef string, passVarsToForkedPR bool) *gwapitypes.ProjectResponse {
project, _, err := gwClient.UpdateProject(ctx, projectRef, &gwapitypes.UpdateProjectRequest{
PassVarsToForkedPR: util.BoolP(passVarsToForkedPR),
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
return project
}
func push(t *testing.T, config, cloneURL, remoteToken, message string, pushNewBranch bool) {
gitfs := memfs.New() gitfs := memfs.New()
f, err := gitfs.Create(".agola/config.jsonnet") f, err := gitfs.Create(".agola/config.jsonnet")
if err != nil { if err != nil {
@ -521,6 +590,52 @@ func push(t *testing.T, config, cloneURL, remoteToken, message string) {
t.Fatalf("unexpected err: %v", err) t.Fatalf("unexpected err: %v", err)
} }
if pushNewBranch {
// change worktree and push to a new branch
headRef, err := r.Head()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
ref := plumbing.NewHashReference("refs/heads/new-branch", headRef.Hash())
err = r.Storer.SetReference(ref)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
f, err = gitfs.Create("file1")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if _, err = f.Write([]byte("my file content")); err != nil {
t.Fatalf("unexpected err: %v", err)
}
if _, err := wt.Add("file1"); err != nil {
t.Fatalf("unexpected err: %v", err)
}
_, err = wt.Commit("add file1", &git.CommitOptions{
Author: &object.Signature{
Name: "user01",
Email: "user01@example.com",
When: time.Now(),
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := r.Push(&git.PushOptions{
RemoteName: "origin",
RefSpecs: []gitconfig.RefSpec{
gitconfig.RefSpec("refs/heads/new-branch:refs/heads/new-branch"),
},
Auth: &http.BasicAuth{
Username: giteaUser01,
Password: remoteToken,
},
}); err != nil {
t.Fatalf("unexpected err: %v", err)
}
}
} }
func TestPush(t *testing.T) { func TestPush(t *testing.T) {
@ -686,7 +801,7 @@ func TestPush(t *testing.T) {
giteaRepo, project := createProject(ctx, t, giteaClient, gwClient) giteaRepo, project := createProject(ctx, t, giteaClient, gwClient)
push(t, tt.config, giteaRepo.CloneURL, giteaToken, tt.message) push(t, tt.config, giteaRepo.CloneURL, giteaToken, tt.message, false)
_ = testutil.Wait(30*time.Second, func() (bool, error) { _ = testutil.Wait(30*time.Second, func() (bool, error) {
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false) runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
@ -1282,3 +1397,286 @@ func TestDirectRunLogs(t *testing.T) {
}) })
} }
} }
func TestPullRequest(t *testing.T) {
config := `
{
runs: [
{
name: 'run01',
tasks: [
{
name: 'task01',
runtime: {
containers: [
{
image: 'alpine/git',
},
],
},
environment: {
MYPASSWORD: { from_variable: 'mypassword' },
},
steps: [
{ type: 'clone' },
{ type: 'run', command: 'echo -n $MYPASSWORD' },
],
},
],
when: {
ref: '#refs/pull/\\d+/head#',
},
},
],
}
`
tests := []struct {
name string
passVarsToForkedPR bool
prFromSameRepo bool
expected string
}{
{
name: "test PR from same repowith PassVarsToForkedPR set to false",
passVarsToForkedPR: false,
prFromSameRepo: true,
expected: "mysupersecretpassword",
},
{
name: "test PR from same repo with PassVarsToForkedPR set to true",
passVarsToForkedPR: true,
prFromSameRepo: true,
expected: "mysupersecretpassword",
},
{
name: "test PR from forked repo with PassVarsToForkedPR set to false",
passVarsToForkedPR: false,
prFromSameRepo: false,
expected: "",
},
{
name: "test PR from forked repo with PassVarsToForkedPR set to true",
passVarsToForkedPR: true,
prFromSameRepo: false,
expected: "mysupersecretpassword",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dir, err := ioutil.TempDir("", "agola")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
defer os.RemoveAll(dir)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
tetcd, tgitea, c := setup(ctx, t, dir)
defer shutdownGitea(tgitea)
defer shutdownEtcd(tetcd)
giteaAPIURL := fmt.Sprintf("http://%s:%s", tgitea.HTTPListenAddress, tgitea.HTTPPort)
giteaToken, token := createLinkedAccount(ctx, t, tgitea, c)
giteaClient := gitea.NewClient(giteaAPIURL, giteaToken)
gwClient := gwclient.NewClient(c.Gateway.APIExposedURL, token)
giteaRepo, project := createProject(ctx, t, giteaClient, gwClient)
project = updateProject(ctx, t, giteaClient, gwClient, project.ID, tt.passVarsToForkedPR)
//create project secret
secretData := map[string]string{"mypassword": "mysupersecretpassword"}
sreq := &gwapitypes.CreateSecretRequest{
Name: "mysecret",
Type: gwapitypes.SecretTypeInternal,
Data: secretData,
}
secret, _, err := gwClient.CreateProjectSecret(context.TODO(), project.ID, sreq)
if err != nil {
t.Fatal("failed to create project secret: %w", err)
}
// create project variable
rvalues := []gwapitypes.VariableValueRequest{}
rvalues = append(rvalues, gwapitypes.VariableValueRequest{
SecretName: secret.Name,
SecretVar: "mypassword",
})
vreq := &gwapitypes.CreateVariableRequest{
Name: "mypassword",
Values: rvalues,
}
_, _, err = gwClient.CreateProjectVariable(context.TODO(), project.ID, vreq)
if err != nil {
t.Fatal("failed to create project variable: %w", err)
}
if tt.prFromSameRepo {
// create PR from branch on same repo
push(t, config, giteaRepo.CloneURL, giteaToken, "commit", true)
prOpts := gitea.CreatePullRequestOption{
Head: "new-branch",
Base: "master",
Title: "add file1 from new-branch on same repo",
}
_, err = giteaClient.CreatePullRequest(giteaUser01, "repo01", prOpts)
if err != nil {
t.Fatal("failed to create pull request: %w", err)
}
} else {
// create PR from forked repo
push(t, config, giteaRepo.CloneURL, giteaToken, "commit", false)
userOpts := gitea.CreateUserOption{
Username: giteaUser02,
Password: "password",
Email: "user02@example.com",
MustChangePassword: util.BoolP(false),
}
_, err := giteaClient.AdminCreateUser(userOpts)
if err != nil {
t.Fatal("failed to create user02: %w", err)
}
giteaUser02Token, err := giteaClient.CreateAccessToken(giteaUser02, "password", gitea.CreateAccessTokenOption{Name: "token01"})
if err != nil {
t.Fatalf("failed to create token for user02: %v", err)
}
giteaUser02Client := gitea.NewClient(giteaAPIURL, giteaUser02Token.Token)
giteaForkedRepo, err := giteaUser02Client.CreateFork(giteaUser01, "repo01", gitea.CreateForkOption{})
if err != nil {
t.Fatal("failed to fork repo01: %w", err)
}
gitfs := memfs.New()
r, err := git.Clone(memory.NewStorage(), gitfs, &git.CloneOptions{
Auth: &http.BasicAuth{
Username: giteaUser02,
Password: giteaUser02Token.Token,
},
URL: giteaForkedRepo.CloneURL,
})
if err != nil {
t.Fatalf("failed to clone forked repo: %v", err)
}
wt, err := r.Worktree()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
f, err := gitfs.Create("file2")
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if _, err = f.Write([]byte("file2 content")); err != nil {
t.Fatalf("unexpected err: %v", err)
}
if _, err := wt.Add("file2"); err != nil {
t.Fatalf("unexpected err: %v", err)
}
_, err = wt.Commit("commit from user02", &git.CommitOptions{
Author: &object.Signature{
Name: giteaUser02,
Email: "user02@example.com",
When: time.Now(),
},
})
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
if err := r.Push(&git.PushOptions{
RemoteName: "origin",
RefSpecs: []gitconfig.RefSpec{
gitconfig.RefSpec("refs/heads/master:refs/heads/master"),
},
Auth: &http.BasicAuth{
Username: giteaUser02,
Password: giteaUser02Token.Token,
},
}); err != nil {
t.Fatalf("unexpected err: %v", err)
}
prOpts := gitea.CreatePullRequestOption{
Head: "user02:master",
Base: "master",
Title: "add file1 from master on forked repo",
}
_, err = giteaUser02Client.CreatePullRequest(giteaUser01, "repo01", prOpts)
if err != nil {
t.Fatal("failed to create pull request: %w", err)
}
}
_ = testutil.Wait(30*time.Second, func() (bool, error) {
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
if err != nil {
return false, nil
}
if len(runs) == 0 {
return false, nil
}
run := runs[0]
if run.Phase != rstypes.RunPhaseFinished {
return false, nil
}
return true, nil
})
runs, _, err := gwClient.GetRuns(ctx, nil, nil, []string{path.Join("/project", project.ID)}, nil, "", 0, false)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
t.Logf("runs: %s", util.Dump(runs))
run, _, err := gwClient.GetRun(ctx, runs[0].ID)
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
var task *gwapitypes.RunResponseTask
for _, t := range run.Tasks {
if t.Name == "task01" {
task = t
break
}
}
if len(runs) > 0 {
run := runs[0]
if run.Phase != rstypes.RunPhaseFinished {
t.Fatalf("expected run phase %q, got %q", rstypes.RunPhaseFinished, run.Phase)
}
if run.Result != rstypes.RunResultSuccess {
t.Fatalf("expected run result %q, got %q", rstypes.RunResultSuccess, run.Result)
}
resp, err := gwClient.GetLogs(ctx, run.ID, task.ID, false, 1, false)
if err != nil {
t.Fatalf("failed to get log: %v", err)
}
defer resp.Body.Close()
mypassword, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("failed to read log: %v", err)
}
if tt.expected != string(mypassword) {
t.Fatalf("expected mypassword %q, got %q", tt.expected, string(mypassword))
}
}
})
}
}